0003-add-a-gallery-cell-type-7344.patch
combo/apps/gallery/__init__.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import django.apps |
|
18 | ||
19 |
class AppConfig(django.apps.AppConfig): |
|
20 |
name = 'combo.apps.gallery' |
|
21 | ||
22 |
def get_after_manager_urls(self): |
|
23 |
from . import urls |
|
24 |
return urls.manager_urlpatterns |
|
25 | ||
26 |
default_app_config = 'combo.apps.gallery.AppConfig' |
combo/apps/gallery/forms.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django import forms |
|
18 |
from django.utils.translation import ugettext_lazy as _ |
|
19 | ||
20 |
from .models import Image |
|
21 | ||
22 |
class ImageAddForm(forms.ModelForm): |
|
23 |
class Meta: |
|
24 |
model = Image |
|
25 |
fields = ('image', 'title',) |
|
26 | ||
27 | ||
28 |
class ImageEditForm(forms.ModelForm): |
|
29 |
class Meta: |
|
30 |
model = Image |
|
31 |
fields = ('title',) |
combo/apps/gallery/migrations/0001_initial.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('auth', '0001_initial'), |
|
11 |
('data', '0005_auto_20150226_0903'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.CreateModel( |
|
16 |
name='GalleryCell', |
|
17 |
fields=[ |
|
18 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
19 |
('placeholder', models.CharField(max_length=20)), |
|
20 |
('order', models.PositiveIntegerField()), |
|
21 |
('slug', models.SlugField(verbose_name='Slug', blank=True)), |
|
22 |
('public', models.BooleanField(default=True, verbose_name='Public')), |
|
23 |
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)), |
|
24 |
('page', models.ForeignKey(to='data.Page')), |
|
25 |
], |
|
26 |
options={ |
|
27 |
'verbose_name': 'Gallery', |
|
28 |
}, |
|
29 |
bases=(models.Model,), |
|
30 |
), |
|
31 |
migrations.CreateModel( |
|
32 |
name='Image', |
|
33 |
fields=[ |
|
34 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
35 |
('image', models.ImageField(upload_to=b'uploads/gallery/%Y/%m/', verbose_name='Image')), |
|
36 |
('order', models.PositiveIntegerField()), |
|
37 |
('gallery', models.ForeignKey(verbose_name='Gallery', to='gallery.GalleryCell')), |
|
38 |
], |
|
39 |
options={ |
|
40 |
'ordering': ['order'], |
|
41 |
}, |
|
42 |
bases=(models.Model,), |
|
43 |
), |
|
44 |
] |
combo/apps/gallery/migrations/0002_image_title.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('gallery', '0001_initial'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='image', |
|
16 |
name='title', |
|
17 |
field=models.CharField(max_length=50, verbose_name='Title', blank=True), |
|
18 |
preserve_default=True, |
|
19 |
), |
|
20 |
] |
combo/apps/gallery/models.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django import template |
|
18 |
from django.db import models |
|
19 |
from django.forms import models as model_forms |
|
20 |
from django.utils.translation import ugettext_lazy as _ |
|
21 | ||
22 |
from combo.data.models import CellBase |
|
23 |
from combo.data.library import register_cell_class |
|
24 | ||
25 |
@register_cell_class |
|
26 |
class GalleryCell(CellBase): |
|
27 |
manager_form_template = 'combo/gallery_manager.html' |
|
28 | ||
29 |
class Meta: |
|
30 |
verbose_name = _('Gallery') |
|
31 | ||
32 |
def render(self, context): |
|
33 |
gallery_template = template.loader.get_template('combo/gallery.html') |
|
34 |
return gallery_template.render(context) |
|
35 | ||
36 |
class Image(models.Model): |
|
37 |
gallery = models.ForeignKey(GalleryCell, verbose_name=_('Gallery')) |
|
38 |
image = models.ImageField(_('Image'), |
|
39 |
upload_to='uploads/gallery/%Y/%m/') |
|
40 |
order = models.PositiveIntegerField() |
|
41 |
title = models.CharField(_('Title'), max_length=50, blank=True) |
|
42 | ||
43 |
class Meta: |
|
44 |
ordering = ['order'] |
combo/apps/gallery/static/js/combo.gallery.js | ||
---|---|---|
1 |
function gallery(element) { |
|
2 |
var element_id = '#' + $(element).attr('id'); |
|
3 |
$(element).sortable({ |
|
4 |
items: '> li', |
|
5 |
containment: 'parent', |
|
6 |
placeholder: 'empty-image', |
|
7 |
update: function(event, ui) { |
|
8 |
var new_order = $(element).find('> li').map(function() { return $(this).data('object-id'); }).get().join(); |
|
9 |
$.ajax({ |
|
10 |
url: $(element).data('order-url'), |
|
11 |
data: {'new-order': new_order}, |
|
12 |
success: function(data, status) { |
|
13 |
$(element).replaceWith($(data).find(element_id)); |
|
14 |
gallery($(element_id)); |
|
15 |
} |
|
16 |
}); |
|
17 |
} |
|
18 |
}); |
|
19 |
$('.image-delete').on('click', function() { |
|
20 |
$.ajax({ |
|
21 |
url: $(this).attr('href'), |
|
22 |
success: function(data, status) { |
|
23 |
$(element).replaceWith($(data).find(element_id)); |
|
24 |
gallery($(element_id)); |
|
25 |
} |
|
26 |
}); |
|
27 |
return false; |
|
28 |
}); |
|
29 |
}; |
combo/apps/gallery/templates/combo/gallery.html | ||
---|---|---|
1 |
{% load thumbnail %} |
|
2 |
<div class="gallery" id="gallery-{{cell.id}}"> |
|
3 |
{% for image in cell.image_set.all %} |
|
4 |
{% if forloop.first %} |
|
5 |
{% thumbnail image.image "640x480" crop="50% 25%" as im %} |
|
6 |
<div class="first"> |
|
7 |
<img src="{{ im.url }}"/> |
|
8 |
<span>{% if image.title %}{{ image.title }}{% endif %}</span> |
|
9 |
</div> |
|
10 |
<div> |
|
11 |
{% endthumbnail %} |
|
12 |
{% endif %} |
|
13 |
{% thumbnail image.image "60x60" crop="50% 25%" as im %} |
|
14 |
{% thumbnail image.image "640x480" crop="50% 25%" as im_large %} |
|
15 |
<span data-image-large="{{ im_large.url }}"><img src="{{ im.url }}" |
|
16 |
{% if image.title %} title="{{image.title}}" {% endif %}/></span> |
|
17 |
{% endthumbnail %} |
|
18 |
{% endthumbnail %} |
|
19 |
{% endfor %} |
|
20 |
</div> |
|
21 |
</div> |
|
22 |
<script type="text/javascript"> |
|
23 |
$(function() { |
|
24 |
var $gallery = $('#gallery-{{cell.id}}'); |
|
25 |
$gallery.find('span').on('click', function() { |
|
26 |
$gallery.find('div.first img').attr('src', $(this).data('image-large')); |
|
27 |
}); |
|
28 |
}); |
|
29 |
</script> |
combo/apps/gallery/templates/combo/gallery_image_form.html | ||
---|---|---|
1 |
{% extends "combo/manager_base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar %} |
|
5 |
{% if object.id %} |
|
6 |
<h2>{% trans "Edit Image" %}</h2> |
|
7 |
{% else %} |
|
8 |
<h2>{% trans "New Image" %}</h2> |
|
9 |
{% endif %} |
|
10 |
{% endblock %} |
|
11 | ||
12 |
{% block content %} |
|
13 | ||
14 |
<form method="post" enctype="multipart/form-data"> |
|
15 |
{% csrf_token %} |
|
16 |
{{ form.as_p }} |
|
17 |
<div class="buttons"> |
|
18 |
<button>{% trans "Save" %}</button> |
|
19 |
{% if object.id %} |
|
20 |
<a class="cancel" href="{{ object.get_absolute_url }}">{% trans 'Cancel' %}</a> |
|
21 |
{% else %} |
|
22 |
<a class="cancel" href="{% url 'combo-manager-homepage' %}">{% trans 'Cancel' %}</a> |
|
23 |
{% endif %} |
|
24 |
</div> |
|
25 |
</form> |
|
26 |
{% endblock %} |
combo/apps/gallery/templates/combo/gallery_manager.html | ||
---|---|---|
1 |
{% extends 'combo/cell_form.html' %} |
|
2 |
{% load static thumbnail i18n %} |
|
3 | ||
4 |
{% block cell-form %} |
|
5 |
<ul class="gallery" id="gallery-{{cell.id}}" data-order-url="{% url 'combo-gallery-image-order' gallery_pk=cell.id %}"> |
|
6 |
{% for image in cell.image_set.all %} |
|
7 |
<li data-object-id="{{image.id}}"> |
|
8 |
{% thumbnail image.image "120x120" crop="50% 25%" as im %} |
|
9 |
<img height="120" src="{{ im.url }}"/> |
|
10 |
{% endthumbnail %} |
|
11 |
<span class="image-actions"> |
|
12 |
<a rel="popup" class="image-edit icon-edit" href="{% url 'combo-gallery-image-edit' gallery_pk=cell.id pk=image.id %}"></a> |
|
13 |
<a class="image-delete icon-remove-sign" href="{% url 'combo-gallery-image-delete' gallery_pk=cell.id pk=image.id %}"></a> |
|
14 |
</span> |
|
15 |
</li> |
|
16 |
{% endfor %} |
|
17 |
</ul> |
|
18 | ||
19 |
<script src="{% static "js/combo.gallery.js" %}"></script> |
|
20 |
<script>gallery($('#gallery-{{cell.id}}'));</script> |
|
21 | ||
22 |
{% endblock %} |
|
23 | ||
24 | ||
25 |
{% block cell-buttons %} |
|
26 |
<a class="image-add" rel="popup" href="{% url 'combo-gallery-image-add' gallery_pk=cell.id %}">{% trans 'New Image' %}</a> |
|
27 |
| |
|
28 |
{{ block.super }} |
|
29 |
{% endblock %} |
combo/apps/gallery/templates/combo/manager/gallery/gallerycell.html | ||
---|---|---|
1 |
HLLOW |
combo/apps/gallery/urls.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf.urls import patterns, url |
|
18 | ||
19 |
from . import views |
|
20 | ||
21 |
manager_urlpatterns = patterns('', |
|
22 |
url('^gallery/(?P<gallery_pk>\w+)/images/add/$', views.image_add, |
|
23 |
name='combo-gallery-image-add'), |
|
24 |
url('^gallery/(?P<gallery_pk>\w+)/order$', views.image_order, |
|
25 |
name='combo-gallery-image-order'), |
|
26 |
url('^gallery/(?P<gallery_pk>\w+)/images/(?P<pk>\w+)/edit$', views.image_edit, |
|
27 |
name='combo-gallery-image-edit'), |
|
28 |
url('^gallery/(?P<gallery_pk>\w+)/images/(?P<pk>\w+)/delete$', views.image_delete, |
|
29 |
name='combo-gallery-image-delete'), |
|
30 |
) |
combo/apps/gallery/views.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.core.urlresolvers import reverse, reverse_lazy |
|
18 |
from django.shortcuts import redirect |
|
19 |
from django.views.generic import (TemplateView, RedirectView, DetailView, |
|
20 |
CreateView, UpdateView, ListView, DeleteView, FormView) |
|
21 | ||
22 |
from .models import Image, GalleryCell |
|
23 |
from .forms import ImageAddForm, ImageEditForm |
|
24 | ||
25 |
class ImageAddView(CreateView): |
|
26 |
model = Image |
|
27 |
template_name = 'combo/gallery_image_form.html' |
|
28 |
form_class = ImageAddForm |
|
29 | ||
30 |
def form_valid(self, form): |
|
31 |
form.instance.gallery_id = self.kwargs.get('gallery_pk') |
|
32 |
other_images = form.instance.gallery.image_set.all() |
|
33 |
if other_images: |
|
34 |
form.instance.order = max([x.order for x in other_images]) + 1 |
|
35 |
else: |
|
36 |
form.instance.order = 0 |
|
37 |
return super(ImageAddView, self).form_valid(form) |
|
38 | ||
39 |
def get_success_url(self): |
|
40 |
return reverse('combo-manager-page-view', kwargs={'pk': self.object.gallery.page.id}) |
|
41 | ||
42 |
image_add = ImageAddView.as_view() |
|
43 | ||
44 | ||
45 |
class ImageEditView(UpdateView): |
|
46 |
model = Image |
|
47 |
template_name = 'combo/gallery_image_form.html' |
|
48 |
form_class = ImageEditForm |
|
49 | ||
50 |
def get_success_url(self): |
|
51 |
return reverse('combo-manager-page-view', kwargs={'pk': self.object.gallery.page.id}) |
|
52 | ||
53 |
image_edit = ImageEditView.as_view() |
|
54 | ||
55 | ||
56 |
def image_delete(request, gallery_pk, pk): |
|
57 |
gallery = GalleryCell.objects.get(id=gallery_pk) |
|
58 |
Image.objects.get(id=pk).delete() |
|
59 |
return redirect(reverse('combo-manager-page-view', kwargs={'pk': gallery.page.id})) |
|
60 | ||
61 | ||
62 |
def image_order(request, gallery_pk): |
|
63 |
gallery = GalleryCell.objects.get(id=gallery_pk) |
|
64 |
new_order = [int(x) for x in request.GET['new-order'].split(',')] |
|
65 |
for image in gallery.image_set.all(): |
|
66 |
image.order = new_order.index(image.id)+1 |
|
67 |
image.save() |
|
68 |
return redirect(reverse('combo-manager-page-view', kwargs={'pk': gallery.page.id})) |
combo/manager/static/css/combo.manager.css | ||
---|---|---|
139 | 139 |
margin-left: 2em; |
140 | 140 |
} |
141 | 141 | |
142 |
.icon-eye-open:before { content: "\f06e "; } |
|
142 |
.icon-eye-open:before { content: "\f06e "; } |
|
143 |
.icon-edit:before { content: "\f044"; } |
|
143 | 144 | |
144 | 145 |
div.objects-list > div { |
145 | 146 |
border: 1px solid #eee; |
... | ... | |
253 | 254 |
div#asset-cmds { |
254 | 255 |
text-align: right; |
255 | 256 |
} |
257 | ||
258 |
ul.gallery { |
|
259 |
list-style: none; |
|
260 |
margin: 0; |
|
261 |
padding: 0; |
|
262 |
} |
|
263 | ||
264 |
ul.gallery li { |
|
265 |
display: inline-block; |
|
266 |
margin: 0.5ex; |
|
267 |
position: relative; |
|
268 |
} |
|
269 | ||
270 |
ul.gallery li img { |
|
271 |
border: 1px solid #aaa; |
|
272 |
background: white; |
|
273 |
padding: 2px; |
|
274 |
} |
|
275 | ||
276 |
ul.gallery li.empty-image { |
|
277 |
width: 122px; |
|
278 |
} |
|
279 | ||
280 |
ul.gallery li span.image-actions { |
|
281 |
position: absolute; |
|
282 |
bottom: 5px; |
|
283 |
right: 5px; |
|
284 |
font-size: 150%; |
|
285 |
} |
|
256 |
- |