add a gallery cell type (#7344)

This commit is contained in:
Frédéric Péters 2015-05-25 12:59:41 +02:00
parent ed5b366a6b
commit 134ce73db1
14 changed files with 409 additions and 0 deletions

View File

@ -0,0 +1,26 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import django.apps
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.gallery'
def get_after_manager_urls(self):
from . import urls
return urls.manager_urlpatterns
default_app_config = 'combo.apps.gallery.AppConfig'

View File

@ -0,0 +1,31 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import forms
from django.utils.translation import ugettext_lazy as _
from .models import Image
class ImageAddForm(forms.ModelForm):
class Meta:
model = Image
fields = ('image', 'title',)
class ImageEditForm(forms.ModelForm):
class Meta:
model = Image
fields = ('title',)

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
('data', '0005_auto_20150226_0903'),
]
operations = [
migrations.CreateModel(
name='GalleryCell',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(verbose_name='Slug', blank=True)),
('public', models.BooleanField(default=True, verbose_name='Public')),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
('page', models.ForeignKey(to='data.Page')),
],
options={
'verbose_name': 'Gallery',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Image',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('image', models.ImageField(upload_to=b'uploads/gallery/%Y/%m/', verbose_name='Image')),
('order', models.PositiveIntegerField()),
('gallery', models.ForeignKey(verbose_name='Gallery', to='gallery.GalleryCell')),
],
options={
'ordering': ['order'],
},
bases=(models.Model,),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('gallery', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='image',
name='title',
field=models.CharField(max_length=50, verbose_name='Title', blank=True),
preserve_default=True,
),
]

View File

@ -0,0 +1,44 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import template
from django.db import models
from django.forms import models as model_forms
from django.utils.translation import ugettext_lazy as _
from combo.data.models import CellBase
from combo.data.library import register_cell_class
@register_cell_class
class GalleryCell(CellBase):
manager_form_template = 'combo/gallery_manager.html'
class Meta:
verbose_name = _('Gallery')
def render(self, context):
gallery_template = template.loader.get_template('combo/gallery.html')
return gallery_template.render(context)
class Image(models.Model):
gallery = models.ForeignKey(GalleryCell, verbose_name=_('Gallery'))
image = models.ImageField(_('Image'),
upload_to='uploads/gallery/%Y/%m/')
order = models.PositiveIntegerField()
title = models.CharField(_('Title'), max_length=50, blank=True)
class Meta:
ordering = ['order']

View File

@ -0,0 +1,29 @@
function gallery(element) {
var element_id = '#' + $(element).attr('id');
$(element).sortable({
items: '> li',
containment: 'parent',
placeholder: 'empty-image',
update: function(event, ui) {
var new_order = $(element).find('> li').map(function() { return $(this).data('object-id'); }).get().join();
$.ajax({
url: $(element).data('order-url'),
data: {'new-order': new_order},
success: function(data, status) {
$(element).replaceWith($(data).find(element_id));
gallery($(element_id));
}
});
}
});
$('.image-delete').on('click', function() {
$.ajax({
url: $(this).attr('href'),
success: function(data, status) {
$(element).replaceWith($(data).find(element_id));
gallery($(element_id));
}
});
return false;
});
};

View File

@ -0,0 +1,29 @@
{% load thumbnail %}
<div class="gallery" id="gallery-{{cell.id}}">
{% for image in cell.image_set.all %}
{% if forloop.first %}
{% thumbnail image.image "640x480" crop="50% 25%" as im %}
<div class="first">
<img src="{{ im.url }}"/>
<span>{% if image.title %}{{ image.title }}{% endif %}</span>
</div>
<div>
{% endthumbnail %}
{% endif %}
{% thumbnail image.image "60x60" crop="50% 25%" as im %}
{% thumbnail image.image "640x480" crop="50% 25%" as im_large %}
<span data-image-large="{{ im_large.url }}"><img src="{{ im.url }}"
{% if image.title %} title="{{image.title}}" {% endif %}/></span>
{% endthumbnail %}
{% endthumbnail %}
{% endfor %}
</div>
</div>
<script type="text/javascript">
$(function() {
var $gallery = $('#gallery-{{cell.id}}');
$gallery.find('span').on('click', function() {
$gallery.find('div.first img').attr('src', $(this).data('image-large'));
});
});
</script>

View File

@ -0,0 +1,26 @@
{% extends "combo/manager_base.html" %}
{% load i18n %}
{% block appbar %}
{% if object.id %}
<h2>{% trans "Edit Image" %}</h2>
{% else %}
<h2>{% trans "New Image" %}</h2>
{% endif %}
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button>{% trans "Save" %}</button>
{% if object.id %}
<a class="cancel" href="{{ object.get_absolute_url }}">{% trans 'Cancel' %}</a>
{% else %}
<a class="cancel" href="{% url 'combo-manager-homepage' %}">{% trans 'Cancel' %}</a>
{% endif %}
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends 'combo/cell_form.html' %}
{% load static thumbnail i18n %}
{% block cell-form %}
<ul class="gallery" id="gallery-{{cell.id}}" data-order-url="{% url 'combo-gallery-image-order' gallery_pk=cell.id %}">
{% for image in cell.image_set.all %}
<li data-object-id="{{image.id}}">
{% thumbnail image.image "120x120" crop="50% 25%" as im %}
<img height="120" src="{{ im.url }}"/>
{% endthumbnail %}
<span class="image-actions">
<a rel="popup" class="image-edit icon-edit" href="{% url 'combo-gallery-image-edit' gallery_pk=cell.id pk=image.id %}"></a>
<a class="image-delete icon-remove-sign" href="{% url 'combo-gallery-image-delete' gallery_pk=cell.id pk=image.id %}"></a>
</span>
</li>
{% endfor %}
</ul>
<script src="{% static "js/combo.gallery.js" %}"></script>
<script>gallery($('#gallery-{{cell.id}}'));</script>
{% endblock %}
{% block cell-buttons %}
<a class="image-add" rel="popup" href="{% url 'combo-gallery-image-add' gallery_pk=cell.id %}">{% trans 'New Image' %}</a>
|
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1 @@
HLLOW

View File

@ -0,0 +1,30 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import patterns, url
from . import views
manager_urlpatterns = patterns('',
url('^gallery/(?P<gallery_pk>\w+)/images/add/$', views.image_add,
name='combo-gallery-image-add'),
url('^gallery/(?P<gallery_pk>\w+)/order$', views.image_order,
name='combo-gallery-image-order'),
url('^gallery/(?P<gallery_pk>\w+)/images/(?P<pk>\w+)/edit$', views.image_edit,
name='combo-gallery-image-edit'),
url('^gallery/(?P<gallery_pk>\w+)/images/(?P<pk>\w+)/delete$', views.image_delete,
name='combo-gallery-image-delete'),
)

View File

@ -0,0 +1,68 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse, reverse_lazy
from django.shortcuts import redirect
from django.views.generic import (TemplateView, RedirectView, DetailView,
CreateView, UpdateView, ListView, DeleteView, FormView)
from .models import Image, GalleryCell
from .forms import ImageAddForm, ImageEditForm
class ImageAddView(CreateView):
model = Image
template_name = 'combo/gallery_image_form.html'
form_class = ImageAddForm
def form_valid(self, form):
form.instance.gallery_id = self.kwargs.get('gallery_pk')
other_images = form.instance.gallery.image_set.all()
if other_images:
form.instance.order = max([x.order for x in other_images]) + 1
else:
form.instance.order = 0
return super(ImageAddView, self).form_valid(form)
def get_success_url(self):
return reverse('combo-manager-page-view', kwargs={'pk': self.object.gallery.page.id})
image_add = ImageAddView.as_view()
class ImageEditView(UpdateView):
model = Image
template_name = 'combo/gallery_image_form.html'
form_class = ImageEditForm
def get_success_url(self):
return reverse('combo-manager-page-view', kwargs={'pk': self.object.gallery.page.id})
image_edit = ImageEditView.as_view()
def image_delete(request, gallery_pk, pk):
gallery = GalleryCell.objects.get(id=gallery_pk)
Image.objects.get(id=pk).delete()
return redirect(reverse('combo-manager-page-view', kwargs={'pk': gallery.page.id}))
def image_order(request, gallery_pk):
gallery = GalleryCell.objects.get(id=gallery_pk)
new_order = [int(x) for x in request.GET['new-order'].split(',')]
for image in gallery.image_set.all():
image.order = new_order.index(image.id)+1
image.save()
return redirect(reverse('combo-manager-page-view', kwargs={'pk': gallery.page.id}))

View File

@ -188,6 +188,9 @@ p#redirection {
float: right;
}
.icon-eye-open:before { content: "\f06e "; }
.icon-edit:before { content: "\f044"; }
#assets-browser {
display: flex;
}
@ -380,3 +383,32 @@ span.error {
#id_sub_slug + span.helptext {
max-width: 35rem;
}
ul.gallery {
list-style: none;
margin: 0;
padding: 0;
}
ul.gallery li {
display: inline-block;
margin: 0.5ex;
position: relative;
}
ul.gallery li img {
border: 1px solid #aaa;
background: white;
padding: 2px;
}
ul.gallery li.empty-image {
width: 122px;
}
ul.gallery li span.image-actions {
position: absolute;
bottom: 5px;
right: 5px;
font-size: 150%;
}