Guilherme Gondim avatar Guilherme Gondim committed c1119ec

add support to the user into the admin upload a ZIP archive with a picture set

Comments (0)

Files changed (10)

 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 from imagekit.admin import AdminThumbnail
+from mptt.admin import MPTTModelAdmin
 
-from galeria.forms import AlbumAdminForm
+from galeria.forms import AlbumForm
 from galeria.models import Album, Picture
 
 
 class PictureAdmin(admin.ModelAdmin):
+    change_list_template = 'galeria/admin/change_list.html'
     list_display = ('thumbnail', 'title', 'is_public', 'album', 'date_added')
     list_display_links = ('title',)
     list_editable = ('album', 'is_public')
 class InlinePictureAdmin(admin.TabularInline):
     extra = 1
     model = Picture
+    ordering = ('-date_added',)
     prepopulated_fields = {'slug': ('title',)}
     formfield_overrides = {
         models.TextField: {'widget': forms.Textarea(attrs={'rows':3,'cols':30})}
     }
 
-class AlbumAdmin(admin.ModelAdmin):
-    form = AlbumAdminForm
+class AlbumAdmin(MPTTModelAdmin):
+    change_list_template = 'galeria/admin/change_list.html'
+    form = AlbumForm
     inlines = [InlinePictureAdmin]
     list_display = ('title', 'album_cover', 'is_public', 'order')
     list_editable = ('is_public', 'order')
     list_filter = ['is_public']
     prepopulated_fields = {'slug': ('title',)}
     search_fields = ('title', 'slug', 'description')
-    #mptt_level_indent = 20
 
     def album_cover(self, obj):
         cover = obj.available_cover
         if not cover:
             return _('<em>Not defined</em>')
-        return '<img src="%s" alt="%s" style="width: 42px;" />' % (cover.cover_image.url, cover.title)
+        html = '<img src="%s" alt="%s" style="width: 42px;" />'
+        return html % (cover.cover_image.url, cover.title)
     album_cover.allow_tags = True
     album_cover.short_description = _('cover')
 
+import os
+import zipfile
+
 from django import forms
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.template.defaultfilters import slugify
+from django.utils.translation import ugettext_lazy as _
+from mptt.forms import TreeNodeChoiceField
+
 from galeria.models import Album, Picture
 
 
-class AlbumAdminForm(forms.ModelForm):
+class AlbumForm(forms.ModelForm):
 
     def __init__(self, *args, **kwargs):
-        super(AlbumAdminForm, self).__init__(*args, **kwargs)
-        descendants_set = self.instance.get_descendants(include_self=True).filter(is_public=True)
-        descendants_ids = descendants_set.values('id')
-        picture_set = Picture.objects.public().filter(album__in=descendants_ids).order_by('-date_added')
-        self.fields['cover'].queryset = picture_set
+        # Set available cover choices for a album
+        super(AlbumForm, self).__init__(*args, **kwargs)
+        descendants_set = self.instance.get_descendants(include_self=True)
+        descendants_ids = descendants_set.filter(is_public=True).values('id')
+        picture_set = Picture.objects.public().filter(album__in=descendants_ids)
+        self.fields['cover'].queryset = picture_set.order_by('-date_added')
 
     class Meta:
         model = Album
+
+    def save(self, *args, **kwargs):
+        album = super(AlbumForm, self).save(*args, **kwargs)
+        return album
+
+
+class ZipUploadForm(forms.Form):
+    zip_archive = forms.FileField(
+        label=_('ZIP archive'),
+        help_text=_('Invalid or corrupted image files inside the ZIP archive '
+                    'are ignored.')
+    )
+    album = TreeNodeChoiceField(
+        label=_('Album'),
+        queryset=Album.objects.all(),
+        empty_label=''
+    )
+
+    def clean_zip_archive(self):
+        zip_archive = self.cleaned_data.get('zip_archive')
+        if zip_archive:
+            try:
+                zfile = zipfile.ZipFile(zip_archive)
+                if zfile.testzip():
+                    raise zipfile.BadZipfile
+                zfile.close()
+            except zipfile.BadZipfile:
+                error_msg = _('File is not a ZIP archive or is corrupted.')
+                raise forms.ValidationError(error_msg)
+        return zip_archive
+
+    def process_zip_archive(self):
+        album = self.cleaned_data.get('album')
+        zip_archive = self.cleaned_data.get('zip_archive')
+        if not zip_archive:
+            return
+
+        zfile = zipfile.ZipFile(zip_archive)
+        picture_list = []
+
+        for filename in zfile.namelist():
+            title = os.path.basename(os.path.splitext(filename)[0])
+            slug = slug=slugify(title)
+            picture = Picture(title=title, slug=slug, is_public=False, album=album)
+            try:
+                content = zfile.read(filename)
+                image_file = SimpleUploadedFile(filename, content)
+                picture.original_image = forms.ImageField().clean(image_file)
+            except forms.ValidationError:
+                continue
+            else:
+                picture_list.append(picture)
+
+        return picture_list
+
+    def save(self):
+        picture_list = self.process_zip_archive()
+        Picture.objects.bulk_create(picture_list)
Add a comment to this file

galeria/locale/en/LC_MESSAGES/django.mo

Binary file modified.

galeria/locale/en/LC_MESSAGES/django.po

 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-05-11 19:57-0300\n"
+"POT-Creation-Date: 2012-05-16 21:45-0300\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: admin.py:22
+#: admin.py:26
 msgid "thumbnail"
 msgstr ""
 
-#: admin.py:45
+#: admin.py:53
 msgid "<em>Not defined</em>"
 msgstr ""
 
-#: admin.py:48 models.py:77
+#: admin.py:57 models.py:77
 msgid "cover"
 msgstr ""
 
+#: forms.py:33
+msgid "ZIP archive"
+msgstr ""
+
+#: forms.py:34
+msgid "Invalid or corrupted image files inside the ZIP archive are ignored."
+msgstr ""
+
+#: forms.py:38
+msgid "Album"
+msgstr ""
+
+#: forms.py:52
+msgid "File is not a ZIP archive or is corrupted."
+msgstr ""
+
 #: models.py:31
 msgid "Descending by addition date"
 msgstr ""
 #: models.py:181
 msgid "pictures"
 msgstr ""
+
+#: views.py:45
+msgid "ZIP Upload"
+msgstr ""
+
+#: templates/galeria/admin/change_list.html:7
+#: templates/galeria/admin/zip_upload_form.html:11
+msgid "Upload a ZIP archive with a set of pictures"
+msgstr ""
+
+#: templates/galeria/admin/change_list.html:7
+msgid "Upload ZIP"
+msgstr ""
+
+#: templates/galeria/admin/zip_upload_form.html:12
+msgid ""
+"Note: all pictures will be non-public by default, so you can edit its "
+"properties before publish they to the web."
+msgstr ""
+
+#: templates/galeria/admin/zip_upload_form.html:15
+msgid "Please correct the error below."
+msgstr ""
+
+#: templates/galeria/admin/zip_upload_form.html:36
+msgid "Upload"
+msgstr ""
Add a comment to this file

galeria/locale/pt_BR/LC_MESSAGES/django.mo

Binary file modified.

galeria/locale/pt_BR/LC_MESSAGES/django.po

 msgstr ""
 "Project-Id-Version: Django Galeria\n"
 "Report-Msgid-Bugs-To: https://bitbucket.org/semente/django-galeria/issues\n"
-"POT-Creation-Date: 2012-05-11 19:57-0300\n"
-"PO-Revision-Date: 2012-05-11 22:57+0000\n"
+"POT-Creation-Date: 2012-05-16 21:45-0300\n"
+"PO-Revision-Date: 2012-05-17 00:50+0000\n"
 "Last-Translator: Guilherme Gondim <semente+transifex@taurinus.org>\n"
 "Language-Team: Portuguese (Brazil) (http://www.transifex.net/projects/p/django-galeria/language/pt_BR/)\n"
 "MIME-Version: 1.0\n"
 "Language: pt_BR\n"
 "Plural-Forms: nplurals=2; plural=(n > 1)\n"
 
-#: admin.py:22
+#: admin.py:26
 msgid "thumbnail"
 msgstr "miniatura"
 
-#: admin.py:45
+#: admin.py:53
 msgid "<em>Not defined</em>"
 msgstr "<em>Não definida</em>"
 
-#: admin.py:48 models.py:77
+#: admin.py:57 models.py:77
 msgid "cover"
 msgstr "capa"
 
+#: forms.py:33
+msgid "ZIP archive"
+msgstr "Arquivo ZIP"
+
+#: forms.py:34
+msgid "Invalid or corrupted image files inside the ZIP archive are ignored."
+msgstr "Arquivos de imagens inválidos ou corrompidos dentro do ZIP são ignorados."
+
+#: forms.py:38
+msgid "Album"
+msgstr "Álbum"
+
+#: forms.py:52
+msgid "File is not a ZIP archive or is corrupted."
+msgstr "O arquivo não é um ZIP ou está corrompido."
+
 #: models.py:31
 msgid "Descending by addition date"
 msgstr "Decrescente por data de adição"
 msgid ""
 "Automatically built from the title. A slug is a short label generally used "
 "in URLs."
-msgstr "Construído automaticamente a partir do título. Um slug é uma pequena etiqueta geralmente utilizada em URLs."
+msgstr "Construído automaticamente a partir do título. Slug é uma pequena etiqueta geralmente utilizada em URLs."
 
 #: models.py:55 models.py:163
 msgid "description"
 #: models.py:181
 msgid "pictures"
 msgstr "imagens"
+
+#: views.py:45
+msgid "ZIP Upload"
+msgstr "Envio de ZIP"
+
+#: templates/galeria/admin/change_list.html:7
+#: templates/galeria/admin/zip_upload_form.html:11
+msgid "Upload a ZIP archive with a set of pictures"
+msgstr "Envie um arquivo ZIP com um conjunto de imagens"
+
+#: templates/galeria/admin/change_list.html:7
+msgid "Upload ZIP"
+msgstr "Enviar ZIP"
+
+#: templates/galeria/admin/zip_upload_form.html:12
+msgid ""
+"Note: all pictures will be non-public by default, so you can edit its "
+"properties before publish they to the web."
+msgstr "Nota: por padrão, as imagens não serão públicas, então você poderá editar suas propriedades antes de publicá-las na web."
+
+#: templates/galeria/admin/zip_upload_form.html:15
+msgid "Please correct the error below."
+msgstr "Por favor, corrija o erro abaixo."
+
+#: templates/galeria/admin/zip_upload_form.html:36
+msgid "Upload"
+msgstr "Enviar"

galeria/templates/galeria/admin/change_list.html

+{% extends "admin/change_list.html" %}
+{% load i18n %}
+
+{% block object-tools-items %}
+{% if not is_popup %}
+<li>
+  <a href="{% url galeria-zip-upload %}" title="{% trans "Upload a ZIP archive with a set of pictures" %}">{% trans "Upload ZIP" %}</a>
+</li>
+{% endif %}
+{{ block.super }}
+{% endblock %}

galeria/templates/galeria/admin/zip_upload_form.html

+{% extends "admin/base_site.html" %}
+{% load i18n admin_static admin_modify %}
+{% load url from future %}
+
+{% block extrastyle %}{{ block.super }}
+<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
+{% endblock %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label='galeria' %}">Galeria</a>
+&rsaquo; {{ title }}
+</div>
+{% endblock %}
+
+{% block content %}
+<div id="content-main">
+  <form enctype="multipart/form-data" method="post" action=".">{% csrf_token %}
+    <p>{% trans "Upload a ZIP archive with a set of pictures" %}</p>
+    <p><b>{% trans "Note: all pictures will be non-public by default, so you can edit its properties before publish they to the web." %}</b></p>
+    <div>
+      {% if zip_upload_form.errors %}
+        <p class="errornote">{% trans "Please correct the error below." %}</p>
+      {% endif %}
+      <fieldset class="module aligned">
+        {% for field in zip_upload_form %}
+          <div class="form-row{% if field.errors %} errors{% endif %}">
+	    <div>
+	      {% if field.errors %}
+	      <ul class="errorlist">
+		  {% for error in field.errors %}
+		    <li>{{ error }}</li>
+		  {% endfor %}
+	        </ul>
+	      {% endif %}
+              <label class="required">{{ field.label }}: </label>{{ field }}
+              {% if field.help_text %}<p class="help">{{ field.help_text|safe }}</p>{% endif %}
+	    </div>
+          </div>
+        {% endfor %}
+      </fieldset>
+      <div class="submit-row">
+	<input class="default" type="submit" name="_upload"
+               value="{% trans "Upload" %}" />
+      </div>
+    </div>
+  </form>
+</div>
+{% endblock %}
 
 urlpatterns = patterns('',
     url(
-        '(?P<album_slug>[-\w]+)/(?P<pk>[0-9]+)/(?P<slug>[-\w]+)/$',
+        '^(?P<album_slug>[-\w]+)/(?P<pk>[0-9]+)/(?P<slug>[-\w]+)/$',
         views.PictureDetail.as_view(),
         name='galeria-picture'
     ),
         name='galeria-album'
     ),
     url('^$', views.AlbumList.as_view(), name='galeria-album-list'),
+
+    url('admin/zip-upload/', views.zip_upload, name='galeria-zip-upload')
 )
+from django import http
+from django.contrib.auth.decorators import login_required
+from django.core.exceptions import PermissionDenied
+from django.core.urlresolvers import reverse
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.utils.translation import ugettext_lazy as _
 from django.views import generic
 
+from galeria.forms import ZipUploadForm
 from galeria.models import Album, Picture
 
 
 
 class PictureDetail(generic.detail.DetailView):
     model = Picture
+
+
+@login_required
+def zip_upload(request, template='galeria/admin/zip_upload_form.html'):
+    if not request.user.has_perm('galeria.add_picture'):
+        raise PermissionDenied
+
+    form = ZipUploadForm()
+
+    if request.method == 'POST':
+        form = ZipUploadForm(request.POST, request.FILES)
+        if form.is_valid():
+            form.save()
+
+            # Send user to album change view to edit the uploaded pictures
+            album = form.cleaned_data['album']
+            url = reverse('admin:galeria_album_change', args=[album.pk])
+            return http.HttpResponseRedirect(url + '#pictures-group')
+
+    context = {
+        'title': _('ZIP Upload'),
+        'zip_upload_form': form
+    }
+    return render_to_response(template, context, context_instance=RequestContext(request))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.