summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLauréline Guérin <zebuline@entrouvert.com>2020-01-07 14:27:44 (GMT)
committerLauréline Guérin <zebuline@entrouvert.com>2020-01-24 09:31:43 (GMT)
commit24619ce6dbaed88f6f8d30771378a98cc95ad8f0 (patch)
treec95e826bfb890cc6f8f8ce1de50f423a2ad2a1c3
parenta00b640090d7030cfb2a2764fc8e32b24145b962 (diff)
downloadcombo-wip/11006-cell-links.zip
combo-wip/11006-cell-links.tar.gz
combo-wip/11006-cell-links.tar.bz2
cells: add a cell type "list of links" (#11006)wip/11006-cell-links
-rw-r--r--combo/apps/wcs/models.py8
-rw-r--r--combo/data/forms.py13
-rw-r--r--combo/data/migrations/0039_link_list_cell.py39
-rw-r--r--combo/data/models.py80
-rw-r--r--combo/data/templates/combo/manager/link-list-cell-form.html46
-rw-r--r--combo/manager/static/css/combo.manager.css38
-rw-r--r--combo/manager/templates/combo/link_cell_form.html22
-rw-r--r--combo/manager/urls.py12
-rw-r--r--combo/manager/views.py144
-rw-r--r--combo/public/templates/combo/link-list-cell.html12
-rw-r--r--tests/test_cells.py61
-rw-r--r--tests/test_manager.py72
-rw-r--r--tests/test_pages.py35
-rw-r--r--tests/test_wcs.py92
14 files changed, 662 insertions, 12 deletions
diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index 1b72259..f17da12 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -36,14 +36,18 @@ from .utils import get_wcs_json, is_wcs_enabled, get_wcs_services
@register_cell_class
class WcsFormCell(CellBase):
- template_name = 'combo/wcs/form.html'
-
formdef_reference = models.CharField(_('Form'), max_length=150)
cached_title = models.CharField(_('Title'), max_length=150)
cached_url = models.URLField(_('URL'))
cached_json = JSONField(blank=True)
+ template_name = 'combo/wcs/form.html'
+ add_as_link_label = _('add a form link')
+ add_link_label = _('New form link')
+ edit_link_label = _('Edit form link')
+ add_as_link_code = 'form-link'
+
is_enabled = classmethod(is_wcs_enabled)
class Meta:
diff --git a/combo/data/forms.py b/combo/data/forms.py
index ffd4c92..5ca614f 100644
--- a/combo/data/forms.py
+++ b/combo/data/forms.py
@@ -18,7 +18,7 @@ import copy
from django import forms
-from .models import Page, ParametersCell, MenuCell, LinkCell, ConfigJsonCell
+from .models import Page, ParametersCell, MenuCell, LinkCell, LinkListCell, ConfigJsonCell
from jsonfield.widgets import JSONWidget
from combo.utils import cache_during_request
@@ -41,6 +41,7 @@ class ParametersForm(forms.Form):
break
return self.cleaned_data
+
class ParametersCellForm(forms.ModelForm):
class Meta:
model = ParametersCell
@@ -61,6 +62,7 @@ def get_page_choices():
pages = Page.get_as_reordered_flat_hierarchy(Page.objects.all())
return [(x.id, '%s %s' % (u'\u00a0' * x.level * 2, x.title)) for x in pages]
+
class MenuCellForm(forms.ModelForm):
class Meta:
model = MenuCell
@@ -82,6 +84,15 @@ class LinkCellForm(forms.ModelForm):
choices=[(None, '-----')] + get_page_choices())
+class LinkListCellForm(forms.ModelForm):
+ class Meta:
+ model = LinkListCell
+ fields = ['title']
+
+ def __init__(self, *args, **kwargs):
+ super(LinkListCellForm, self).__init__(*args, **kwargs)
+
+
class ConfigJsonForm(forms.ModelForm):
formdef = []
diff --git a/combo/data/migrations/0039_link_list_cell.py b/combo/data/migrations/0039_link_list_cell.py
new file mode 100644
index 0000000..78fa341
--- /dev/null
+++ b/combo/data/migrations/0039_link_list_cell.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('auth', '0008_alter_user_username_max_length'),
+ ('data', '0038_increase_jsoncell_url_max_length'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='LinkListCell',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('placeholder', models.CharField(max_length=20)),
+ ('order', models.PositiveIntegerField()),
+ ('slug', models.SlugField(blank=True, verbose_name='Slug')),
+ ('extra_css_class', models.CharField(blank=True, max_length=100, verbose_name='Extra classes for CSS styling')),
+ ('public', models.BooleanField(default=True, verbose_name='Public')),
+ ('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
+ ('last_update_timestamp', models.DateTimeField(auto_now=True)),
+ ('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='Groups')),
+ ('title', models.CharField(max_length=150, verbose_name='Title', blank=True)),
+ ],
+ options={
+ 'verbose_name': 'List of links',
+ },
+ ),
+ migrations.AddField(
+ model_name='linklistcell',
+ name='page',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='data.Page'),
+ ),
+ ]
diff --git a/combo/data/models.py b/combo/data/models.py
index 372726f..bd84afe 100644
--- a/combo/data/models.py
+++ b/combo/data/models.py
@@ -451,6 +451,8 @@ class Page(models.Model):
new_page.groups.set(self.groups.all())
for cell in self.get_cells():
+ if cell.placeholder and cell.placeholder.startswith('_'):
+ continue
cell.duplicate(page_target=new_page)
return new_page
@@ -756,12 +758,14 @@ class CellBase(six.with_metaclass(CellMeta, models.Model)):
def import_subobjects(self, cell_json):
pass
- def duplicate(self, page_target=None):
+ def duplicate(self, page_target=None, placeholder=None):
# clone current cell
new_cell = copy.deepcopy(self)
new_cell.pk = None
# set page
new_cell.page = page_target or self.page
+ # set placeholder
+ new_cell.placeholder = placeholder or new_cell.placeholder
# store new cell
new_cell.save()
@@ -905,6 +909,10 @@ class LinkCell(CellBase):
anchor = models.CharField(_('Anchor'), max_length=150, blank=True)
template_name = 'combo/link-cell.html'
+ add_as_link_label = _('add a link')
+ add_link_label = _('New link')
+ edit_link_label = _('Edit link')
+ add_as_link_code = 'link'
class Meta:
verbose_name = _('Link')
@@ -951,6 +959,76 @@ class LinkCell(CellBase):
@register_cell_class
+class LinkListCell(CellBase):
+ title = models.CharField(_('Title'), max_length=150, blank=True)
+
+ template_name = 'combo/link-list-cell.html'
+ manager_form_template = 'combo/manager/link-list-cell-form.html'
+
+ class Meta:
+ verbose_name = _('List of links')
+
+ @property
+ def link_placeholder(self):
+ return '_linkslist:{}'.format(self.pk)
+
+ def get_items(self):
+ return CellBase.get_cells(
+ page=self.page,
+ placeholder=self.link_placeholder,
+ cell_filter=lambda x: hasattr(x, 'add_as_link_label'))
+
+ def get_additional_label(self):
+ title = self.title
+ if not title:
+ return None
+ return utils.ellipsize(title)
+
+ def get_cell_extra_context(self, context):
+ extra_context = super(LinkListCell, self).get_cell_extra_context(context)
+ links = []
+ for cell in context.get('page_cells', []):
+ if not hasattr(cell, 'add_as_link_label'):
+ continue
+ if not cell.placeholder == self.link_placeholder:
+ continue
+ links.append(cell.get_cell_extra_context(context))
+ extra_context['links'] = links
+ extra_context['title'] = self.title
+ return extra_context
+
+ def get_link_cell_classes(self):
+ return CellBase.get_cell_classes(lambda x: hasattr(x, 'add_as_link_label'))
+
+ def get_default_form_class(self):
+ from .forms import LinkListCellForm
+ return LinkListCellForm
+
+ def render_for_search(self):
+ return ''
+
+ def export_subobjects(self):
+ return {'links': json.loads(
+ serializers.serialize(
+ 'json',
+ self.get_items(),
+ use_natural_foreign_keys=True,
+ use_natural_primary_keys=True)
+ )}
+
+ def import_subobjects(self, cell_json):
+ for link in cell_json['links']:
+ link['fields']['placeholder'] = self.link_placeholder
+ for link in serializers.deserialize('json', json.dumps(cell_json['links'])):
+ link.save()
+
+ def duplicate_m2m(self, new_cell):
+ # duplicate also link items
+ for link in self.get_items():
+ link.duplicate(page_target=new_cell.page, placeholder=new_cell.link_placeholder)
+
+
+@register_cell_class
class FeedCell(CellBase):
title = models.CharField(_('Title'), max_length=150, blank=True)
url = models.CharField(_('URL'), blank=True, max_length=200)
diff --git a/combo/data/templates/combo/manager/link-list-cell-form.html b/combo/data/templates/combo/manager/link-list-cell-form.html
new file mode 100644
index 0000000..a0688ac
--- /dev/null
+++ b/combo/data/templates/combo/manager/link-list-cell-form.html
@@ -0,0 +1,46 @@
+{% extends "combo/cell_form.html" %}
+{% load i18n %}
+
+{% block cell-form %}
+{{ form.as_p }}
+{% with cell.get_items as links %}
+{% if links %}
+<p><label>{% trans "Links:" %}</label></p>
+<div>
+ <ul class="objects-list list-of-links" id="list-of-links-{{ cell.pk }}"
+ data-link-list-order-url="{% url 'combo-manager-link-list-order' page_pk=page.pk cell_reference=cell.get_reference %}">
+ {% for link in links %}
+ <li data-link-item-id="{{ link.pk }}"><span class="handle">⣿</span>
+ <span title="{{ link }}">{{ link|truncatechars:100 }}</span>
+ <a rel="popup" title="{% trans "Edit" %}" class="link-action-icon edit" href="{% url 'combo-manager-page-list-cell-edit-link' page_pk=page.id cell_reference=cell.get_reference link_cell_reference=link.get_reference %}">{% trans "Edit" %}</a>
+ <a rel="popup" title="{% trans "Delete" %}" class="link-action-icon delete" href="{% url 'combo-manager-page-list-cell-delete-link' page_pk=page.id cell_reference=cell.get_reference link_cell_reference=link.get_reference %}">{% trans "Delete" %}</a>
+ </li>
+ {% endfor %}
+ </ul>
+</div>
+<script>
+$(function () {
+ $('#list-of-links-{{ cell.pk }}').sortable({
+ handle: '.handle',
+ update: function(event, ui) {
+ var new_order = Object();
+ $(this).find('li').each(function(i, x) {
+ var suffix = $(x).data('link-item-id');
+ new_order['pos_' + suffix] = i;
+ });
+ $.ajax({
+ url: $(this).data('link-list-order-url'),
+ data: new_order
+ });
+ }
+ });
+});
+</script>
+{% endif %}
+{% endwith %}
+<div class="buttons">
+ {% for klass in cell.get_link_cell_classes %}
+ <a rel="popup" href="{% url 'combo-manager-page-list-cell-add-link' page_pk=page.id cell_reference=cell.get_reference link_code=klass.add_as_link_code %}">{{ klass.add_as_link_label }}</a> {% if not forloop.last %}|{% endif %}
+ {% endfor %}
+</div>
+{% endblock %}
diff --git a/combo/manager/static/css/combo.manager.css b/combo/manager/static/css/combo.manager.css
index 0f8bfc0..eaf7864 100644
--- a/combo/manager/static/css/combo.manager.css
+++ b/combo/manager/static/css/combo.manager.css
@@ -480,3 +480,41 @@ ul.gallery li:last-child a {
text-align: center;
padding: 45px 1ex 1ex 1ex;
}
+
+ul.objects-list.list-of-links li {
+ padding-left: 0;
+}
+
+ul.objects-list.list-of-links li a.link-action-icon {
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 3em;
+ display: block;
+ overflow: hidden;
+ text-decoration: none;
+ border: none;
+ text-indent: -1000px;
+ line-height: 3em;
+}
+
+ul.objects-list.list-of-links li a.link-action-icon::before {
+ font-family: FontAwesome;
+ text-indent: 0px;
+ text-align: center;
+ display: block;
+ width: 100%;
+}
+
+ul.objects-list.list-of-links li a.link-action-icon.delete::before {
+ content: "\f057"; /* remove-sign */
+}
+
+ul.objects-list.list-of-links li a.link-action-icon.edit {
+ right: 3em;
+}
+
+ul.objects-list.list-of-links li a.link-action-icon.edit::before {
+ content: "\f044";
+}
diff --git a/combo/manager/templates/combo/link_cell_form.html b/combo/manager/templates/combo/link_cell_form.html
new file mode 100644
index 0000000..c702251
--- /dev/null
+++ b/combo/manager/templates/combo/link_cell_form.html
@@ -0,0 +1,22 @@
+{% extends "combo/manager_base.html" %}
+{% load i18n %}
+
+{% block appbar %}
+{% if form.instance.pk %}
+<h2>{{ form.instance.edit_link_label }}</h2>
+{% else %}
+<h2>{{ form.instance.add_link_label }}</h2>
+{% endif %}
+{% endblock %}
+
+{% block content %}
+
+<form method="post" enctype="multipart/form-data">
+ {% csrf_token %}
+ {{ form.as_p }}
+ <div class="buttons">
+ <button class="submit-button">{% trans "Save" %}</button>
+ <a class="cancel" href="{% url 'combo-manager-page-view' pk=form.instance.page_id %}">{% trans 'Cancel' %}</a>
+ </div>
+</form>
+{% endblock %}
diff --git a/combo/manager/urls.py b/combo/manager/urls.py
index 66d34ee..a3f55e4 100644
--- a/combo/manager/urls.py
+++ b/combo/manager/urls.py
@@ -75,6 +75,18 @@ urlpatterns = [
name='combo-manager-page-visibility-cell'),
url(r'^pages/(?P<page_pk>\d+)/cell/(?P<cell_reference>[\w_-]+)/label$',
views.page_get_additional_label, name='combo-manager-page-get-additional-label'),
+ url(r'^pages/(?P<page_pk>\d+)/cell/(?P<cell_reference>[\w_-]+)/add-link/(?P<link_code>[\w-]+)$',
+ views.page_list_cell_add_link,
+ name='combo-manager-page-list-cell-add-link'),
+ url(r'^pages/(?P<page_pk>\d+)/cell/(?P<cell_reference>[\w_-]+)/link/(?P<link_cell_reference>[\w_-]+)/$',
+ views.page_list_cell_edit_link,
+ name='combo-manager-page-list-cell-edit-link'),
+ url(r'^pages/(?P<page_pk>\d+)/cell/(?P<cell_reference>[\w_-]+)/link/(?P<link_cell_reference>[\w_-]+)/delete$',
+ views.page_list_cell_delete_link,
+ name='combo-manager-page-list-cell-delete-link'),
+ url(r'^pages/(?P<page_pk>\d+)/cell/(?P<cell_reference>[\w_-]+)/order$',
+ views.link_list_order,
+ name='combo-manager-link-list-order'),
url(r'^pages/(?P<page_pk>\d+)/order$', views.cell_order,
name='combo-manager-cell-order'),
url(r'^pages/order$', views.page_order,
diff --git a/combo/manager/views.py b/combo/manager/views.py
index 5992fa2..8977bc9 100644
--- a/combo/manager/views.py
+++ b/combo/manager/views.py
@@ -33,7 +33,7 @@ from django.views.decorators.csrf import requires_csrf_token
from django.views.generic import (RedirectView, DetailView,
CreateView, UpdateView, ListView, DeleteView, FormView)
-from combo.data.models import Page, CellBase, ParentContentCell, PageSnapshot
+from combo.data.models import Page, CellBase, ParentContentCell, PageSnapshot, LinkListCell
from combo.data.library import get_cell_class
from combo.data.utils import export_site, import_site, MissingGroups
from combo import plugins
@@ -124,7 +124,6 @@ class PageAddChildView(PageAddView):
return super(CreateView, self).get_initial()
def dispatch(self, request, *args, **kwargs):
- print(request.POST)
self.parent = get_object_or_404(Page, pk=kwargs['pk'])
return super(PageAddChildView, self).dispatch(request, *args, **kwargs)
@@ -399,6 +398,7 @@ class PageEditCellView(UpdateView):
def get_context_data(self, **kwargs):
context = super(PageEditCellView, self).get_context_data(**kwargs)
context['cell'] = self.get_object()
+ context['page'] = context['cell'].page
return context
def get_object(self, queryset=None):
@@ -564,3 +564,143 @@ def menu_json(request):
response = HttpResponse(content_type=content_type)
response.write(json_str)
return response
+
+
+class PageListCellAddLinkView(CreateView):
+ template_name = 'combo/link_cell_form.html'
+
+ def dispatch(self, request, *args, **kwargs):
+ try:
+ self.cell = CellBase.get_cell(kwargs['cell_reference'], page=kwargs['page_pk'])
+ except LinkListCell.DoesNotExist:
+ raise Http404
+ for klass in self.cell.get_link_cell_classes():
+ if klass.add_as_link_code == kwargs['link_code']:
+ self.model = klass
+ break
+ if self.model is None:
+ raise Http404
+ return super(PageListCellAddLinkView, self).dispatch(request, *args, **kwargs)
+
+ def get_form_class(self):
+ return self.model().get_default_form_class()
+
+ def get_form_kwargs(self):
+ kwargs = super(PageListCellAddLinkView, self).get_form_kwargs()
+ kwargs['instance'] = self.model(page=self.cell.page, placeholder=self.cell.link_placeholder)
+ return kwargs
+
+ def form_valid(self, form):
+ orders = [x.order for x in self.cell.get_items()]
+ if orders:
+ form.instance.order = max(orders)+1
+ else:
+ form.instance.order = 1
+ return super(PageListCellAddLinkView, self).form_valid(form)
+
+ def get_success_url(self):
+ return '%s#cell-%s' % (
+ reverse('combo-manager-page-view', kwargs={'pk': self.kwargs.get('page_pk')}),
+ self.kwargs['cell_reference'])
+
+
+page_list_cell_add_link = PageListCellAddLinkView.as_view()
+
+
+class PageListCellEditLinkView(UpdateView):
+ template_name = 'combo/link_cell_form.html'
+
+ def dispatch(self, request, *args, **kwargs):
+ try:
+ self.cell = CellBase.get_cell(kwargs['cell_reference'], page=kwargs['page_pk'])
+ except LinkListCell.DoesNotExist:
+ raise Http404
+ try:
+ self.object = CellBase.get_cell(kwargs['link_cell_reference'], page=kwargs['page_pk'])
+ except ObjectDoesNotExist:
+ raise Http404
+ if self.object.placeholder != self.cell.link_placeholder:
+ raise Http404
+ self.model = self.object.__class__
+ return super(PageListCellEditLinkView, self).dispatch(request, *args, **kwargs)
+
+ def get_object(self, *args, **kwargs):
+ return self.object
+
+ def get_form_class(self):
+ return self.model().get_default_form_class()
+
+ def form_valid(self, form):
+ if self.request.is_ajax():
+ self.object = form.save()
+ response = self.form_invalid(form) # avoid redirection
+ else:
+ response = super(PageListCellEditLinkView, self).form_valid(form)
+ PageSnapshot.take(self.cell.page, request=self.request, comment=_('changed cell "%s"') % self.cell)
+ return response
+
+ def get_success_url(self):
+ return '%s#cell-%s' % (
+ reverse('combo-manager-page-view', kwargs={'pk': self.kwargs.get('page_pk')}),
+ self.kwargs['cell_reference'])
+
+
+page_list_cell_edit_link = PageListCellEditLinkView.as_view()
+
+
+class PageListCellDeleteLinkView(DeleteView):
+ template_name = 'combo/generic_confirm_delete.html'
+
+ def dispatch(self, request, *args, **kwargs):
+ try:
+ self.cell = CellBase.get_cell(kwargs['cell_reference'], page=kwargs['page_pk'])
+ except LinkListCell.DoesNotExist:
+ raise Http404
+ try:
+ self.object = CellBase.get_cell(kwargs['link_cell_reference'], page=kwargs['page_pk'])
+ except ObjectDoesNotExist:
+ raise Http404
+ if self.object.placeholder != self.cell.link_placeholder:
+ raise Http404
+ self.model = self.object.__class__
+ return super(PageListCellDeleteLinkView, self).dispatch(request, *args, **kwargs)
+
+ def get_object(self, *args, **kwargs):
+ return self.object
+
+ def delete(self, request, *args, **kwargs):
+ response = super(PageListCellDeleteLinkView, self).delete(request, *args, **kwargs)
+ PageSnapshot.take(self.cell.page, request=self.request, comment=_('changed cell "%s"') % self.cell)
+ return response
+
+ def get_success_url(self):
+ return '%s#cell-%s' % (
+ reverse('combo-manager-page-view', kwargs={'pk': self.kwargs.get('page_pk')}),
+ self.kwargs['cell_reference'])
+
+
+page_list_cell_delete_link = PageListCellDeleteLinkView.as_view()
+
+
+def link_list_order(request, page_pk, cell_reference):
+ try:
+ cell = CellBase.get_cell(cell_reference, page=page_pk)
+ except LinkListCell.DoesNotExist:
+ raise Http404
+
+ has_changes = False
+ for link in cell.get_items():
+ old_order = link.order
+ try:
+ new_order = int(request.GET.get('pos_' + str(link.pk)))
+ except TypeError:
+ continue
+ if new_order != old_order:
+ link.order = new_order
+ has_changes = True
+ link.save(update_fields=['order'])
+
+ if has_changes:
+ PageSnapshot.take(cell.page, request=request, comment=_('reordered cells'))
+
+ return HttpResponse(status=204)
diff --git a/combo/public/templates/combo/link-list-cell.html b/combo/public/templates/combo/link-list-cell.html
new file mode 100644
index 0000000..706d730
--- /dev/null
+++ b/combo/public/templates/combo/link-list-cell.html
@@ -0,0 +1,12 @@
+{% block cell-content %}
+{% spaceless %}
+<div class="links-list">
+ {% if title %}<h2>{{title}}</h2>{% endif %}
+ <ul>
+ {% for link in links %}
+ <li><a href="{{ link.url }}">{{ link.title }}</a></li>
+ {% endfor %}
+ </ul>
+</div>
+{% endspaceless %}
+{% endblock %}
diff --git a/tests/test_cells.py b/tests/test_cells.py
index 89ddb09..90683ef 100644
--- a/tests/test_cells.py
+++ b/tests/test_cells.py
@@ -4,7 +4,10 @@ import os
import pytest
import requests
-from combo.data.models import Page, CellBase, TextCell, LinkCell, MenuCell, JsonCellBase, JsonCell, ConfigJsonCell
+from combo.data.models import (
+ Page, CellBase, TextCell, LinkCell, MenuCell, JsonCellBase,
+ JsonCell, ConfigJsonCell, LinkListCell
+)
from django.conf import settings
from django.db import connection
from django.forms.widgets import Media
@@ -93,6 +96,7 @@ def test_text_cell_variadic_url():
assert 'href=""' in cell.render(ctx)
assert 'href="/plop"' in cell.render(ctx)
+
def test_link_cell():
page = Page(title='example page', slug='example-page')
page.save()
@@ -130,6 +134,37 @@ def test_link_cell():
assert cell.render(ctx).strip() == '<a href="http://example.net/#anchor">altertitle</a>'
+def test_link_list_cell():
+ page = Page.objects.create(title='example page', slug='example-page')
+
+ cell = LinkListCell.objects.create(order=0, page=page)
+ item = LinkCell.objects.create(
+ page=page,
+ placeholder=cell.link_placeholder,
+ title='Example Site',
+ url='http://example.net/',
+ order=0,
+ )
+
+ ctx = {'page_cells': [item]}
+ assert '<ul><li><a href="http://example.net/">Example Site</a></li></ul>' in cell.render(ctx)
+
+ item.title = ''
+ assert '<ul><li><a href="http://example.net/">http://example.net/</a></li></ul>' in cell.render(ctx)
+
+ item.link_page = page
+ assert '<ul><li><a href="/example-page/">example page</a></li></ul>' in cell.render(ctx)
+
+ item.title = 'altertitle'
+ assert '<ul><li><a href="/example-page/">altertitle</a></li></ul>' in cell.render(ctx)
+
+ item.anchor = 'anchor'
+ assert '<ul><li><a href="/example-page/#anchor">altertitle</a></li></ul>' in cell.render(ctx)
+
+ item.link_page = None
+ assert '<ul><li><a href="http://example.net/#anchor">altertitle</a></li></ul>' in cell.render(ctx)
+
+
def test_menu_cell():
Page.objects.all().delete()
parent = page = Page(title='Page1', slug='page1', template_name='standard')
@@ -728,3 +763,27 @@ def test_related_cell_types_tracking():
TextCell(page=page, placeholder='content', order=0, text='hello').save()
assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == set(['data_textcell', 'data_linkcell'])
+
+
+def test_link_list_cell_duplicate():
+ page = Page.objects.create(title='xxx', slug='new', template_name='standard')
+ cell = LinkListCell.objects.create(order=0, page=page)
+ item = LinkCell.objects.create(
+ page=page,
+ placeholder=cell.link_placeholder,
+ title='Example Site',
+ url='http://example.net/',
+ link_page=page,
+ order=1,
+ )
+
+ new_cell = cell.duplicate()
+ assert LinkCell.objects.count() == 2
+ assert len(new_cell.get_items()) == 1
+ new_item = new_cell.get_items()[0]
+ assert new_item.page == page
+ assert new_item.placeholder == new_cell.link_placeholder
+ assert new_item.pk != item.pk
+ assert new_item.title == item.title
+ assert new_item.url == item.url
+ assert new_item.link_page == item.link_page
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 0b9b7ae..58ad4db 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -21,7 +21,7 @@ from webtest import TestApp
from webtest import Upload
from combo.wsgi import application
-from combo.data.models import Page, CellBase, TextCell, LinkCell, ConfigJsonCell, JsonCell, PageSnapshot
+from combo.data.models import Page, CellBase, TextCell, LinkCell, ConfigJsonCell, JsonCell, PageSnapshot, LinkListCell
from combo.apps.assets.models import Asset
from combo.apps.family.models import FamilyInfosCell
from combo.apps.search.models import SearchCell
@@ -1373,3 +1373,73 @@ def test_json_cell_syntax_validation(app, admin_user):
assert 'syntax error' not in resp.text
assert JsonCell.objects.count() == 1
assert JsonCell.objects.first().template_string == '{{ ok }}'
+
+
+def test_add_edit_delete_list_link_item(app, admin_user):
+ Page.objects.all().delete()
+ page = Page.objects.create(title='One', slug='one', template_name='standard')
+ cell = LinkListCell.objects.create(order=0, placeholder='content', page=page)
+ app = login(app)
+ resp = app.get('/manage/pages/%s/' % page.pk)
+
+ resp = resp.click(href='.*/add-link/link$')
+ resp.forms[0]['title'] = 'Hello world'
+ resp.forms[0]['url'] = 'http://example.com'
+ resp = resp.forms[0].submit()
+ assert resp.status_int == 302
+ assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+ assert LinkCell.objects.count() == 1
+ item = LinkCell.objects.get()
+ assert item.title == 'Hello world'
+ assert item.url == 'http://example.com'
+ assert item.page == page
+ assert item.placeholder == cell.link_placeholder
+
+ resp = resp.follow()
+ resp = resp.click(href='.*/link/%s/$' % item.get_reference())
+ resp.forms[0]['title'] = 'Hello world 2'
+ resp.forms[0]['url'] = 'http://example2.com'
+ resp = resp.forms[0].submit()
+ assert resp.status_int == 302
+ assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+ assert LinkCell.objects.count() == 1
+ item.refresh_from_db()
+ assert item.title == 'Hello world 2'
+ assert item.url == 'http://example2.com'
+
+ resp = resp.follow()
+ resp = resp.click(href='.*/link/%s/delete' % item.get_reference())
+ resp = resp.forms[0].submit()
+ assert resp.status_int == 302
+ assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+ assert LinkCell.objects.count() == 0
+
+
+def test_edit_link_list_order(app, admin_user):
+ Page.objects.all().delete()
+ page = Page.objects.create(title='One', slug='one', template_name='standard')
+ cell = LinkListCell.objects.create(order=0, page=page)
+ items = []
+ for i in range(5):
+ items.append(
+ LinkCell.objects.create(
+ page=page,
+ placeholder=cell.link_placeholder,
+ title='Foo %s' % i,
+ url='http://example.net/',
+ link_page=page,
+ order=i+1,
+ )
+ )
+
+ params = []
+ new_order = [2, 3, 1, 4, 5]
+ for i, (item, new_pos) in enumerate(zip(items, new_order)):
+ params.append(('pos_%s' % item.pk, str(new_pos)))
+
+ app = login(app)
+ resp = app.get('/manage/pages/%s/cell/%s/order?%s' % (page.pk, cell.get_reference(), urlencode(params)))
+ assert resp.status_code == 204
+ for i, item in enumerate(items):
+ item.refresh_from_db()
+ assert item.order == new_order[i]
diff --git a/tests/test_pages.py b/tests/test_pages.py
index 5f45670..2eb108f 100644
--- a/tests/test_pages.py
+++ b/tests/test_pages.py
@@ -9,7 +9,7 @@ from django.test import override_settings
from django.test.client import RequestFactory
from django.utils.six import StringIO
from django.utils.timezone import now
-from combo.data.models import Page, PageSnapshot, CellBase, TextCell, LinkCell
+from combo.data.models import Page, PageSnapshot, CellBase, TextCell, LinkCell, LinkListCell
from combo.data.management.commands.import_site import Command as ImportSiteCommand
from combo.data.management.commands.export_site import Command as ExportSiteCommand
from combo.manager.forms import PageVisibilityForm
@@ -182,6 +182,22 @@ def test_import_export_pages_with_links():
cell2 = LinkCell(page=page2, title='foo', placeholder='content', link_page=page, order=1)
cell2.save()
+ cell3 = LinkListCell.objects.create(page=page, placeholder='content', order=2)
+ item1 = LinkCell.objects.create(
+ page=page,
+ placeholder=cell3.link_placeholder,
+ title='Example Site',
+ url='http://example.net/',
+ order=0,
+ )
+ item2 = LinkCell.objects.create(
+ page=page,
+ placeholder=cell3.link_placeholder,
+ title='blah',
+ link_page=page2,
+ order=1,
+ )
+
site_export = [x.get_serialized_page() for x in Page.objects.all()]
Page.objects.all().delete()
@@ -189,8 +205,21 @@ def test_import_export_pages_with_links():
new_page_1 = Page.objects.all().order_by('order')[0]
new_page_2 = Page.objects.all().order_by('order')[1]
- assert CellBase.get_cells(page_id=new_page_1.id)[0].link_page_id == new_page_2.id
- assert CellBase.get_cells(page_id=new_page_2.id)[0].link_page_id == new_page_1.id
+ new_cells_1 = CellBase.get_cells(page_id=new_page_1.id, placeholder='content')
+ new_cells_2 = CellBase.get_cells(page_id=new_page_2.id, placeholder='content')
+ assert new_cells_1[0].link_page_id == new_page_2.id
+ assert new_cells_2[0].link_page_id == new_page_1.id
+ assert len(new_cells_1[1].get_items()) == 2
+ new_item_1 = new_cells_1[1].get_items()[0]
+ assert isinstance(new_item_1, LinkCell)
+ assert new_item_1.title == item1.title
+ assert new_item_1.url == item1.url
+ assert new_item_1.link_page is None
+ new_item_2 = new_cells_1[1].get_items()[1]
+ assert isinstance(new_item_2, LinkCell)
+ assert new_item_2.title == item2.title
+ assert new_item_2.url == ''
+ assert new_item_2.link_page == new_page_2
def test_duplicate_page():
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index 6dabae7..f145129 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -22,7 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse
import mock
-from combo.data.models import Page
+from combo.data.models import CellBase, LinkListCell, Page
from combo.apps.search.engines import engines
from combo.apps.wcs.models import (WcsFormCell, WcsCurrentFormsCell,
WcsFormsOfCategoryCell, WcsCurrentDraftsCell, WcsCategoryCell,
@@ -901,3 +901,93 @@ def test_backoffice_submission_cell_render(context):
result = cell.render(context)
assert '/backoffice/submission/a-private-form/' in result
+
+
+@wcs_present
+def test_manager_link_list_cell_duplicate():
+ page = Page.objects.create(title='xxx', slug='new', template_name='standard')
+ cell = LinkListCell.objects.create(order=0, page=page)
+ item = WcsFormCell.objects.create(
+ page=page,
+ placeholder=cell.link_placeholder,
+ cached_title='A title',
+ cached_url='http://example.com',
+ cached_json={'foo': 'bar'},
+ order=1)
+
+ new_cell = cell.duplicate()
+ assert WcsFormCell.objects.count() == 2
+ assert len(new_cell.get_items()) == 1
+ new_item = new_cell.get_items()[0]
+ assert new_item.page == page
+ assert new_item.placeholder == new_cell.link_placeholder
+ assert new_item.pk != item.pk
+ assert new_item.cached_title == item.cached_title
+ assert new_item.cached_url == item.cached_url
+ assert new_item.cached_json == item.cached_json
+
+
+@wcs_present
+def test_manager_add_edit_delete_list_link_item(app, admin_user):
+ Page.objects.all().delete()
+ page = Page.objects.create(title='One', slug='one', template_name='standard')
+ cell = LinkListCell.objects.create(order=0, placeholder='content', page=page)
+ app = login(app)
+ resp = app.get('/manage/pages/%s/' % page.pk)
+
+ resp = resp.click(href='.*/add-link/form-link$')
+ resp.forms[0]['formdef_reference'] = 'default:form-title'
+ resp = resp.forms[0].submit()
+ assert resp.status_int == 302
+ assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+ assert WcsFormCell.objects.count() == 1
+ item = WcsFormCell.objects.get()
+ assert item.formdef_reference == 'default:form-title'
+ assert item.page == page
+ assert item.placeholder == cell.link_placeholder
+
+ resp = resp.follow()
+ resp = resp.click(href='.*/link/%s/$' % item.get_reference())
+ resp.forms[0]['formdef_reference'] = 'default:a-private-form'
+ resp = resp.forms[0].submit()
+ assert resp.status_int == 302
+ assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+ assert WcsFormCell.objects.count() == 1
+ item.refresh_from_db()
+ assert item.formdef_reference == 'default:a-private-form'
+
+ resp = resp.follow()
+ resp = resp.click(href='.*/link/%s/delete' % item.get_reference())
+ resp = resp.forms[0].submit()
+ assert resp.status_int == 302
+ assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+ assert WcsFormCell.objects.count() == 0
+
+
+def test_import_export_pages_with_links():
+ page = Page(title=u'bar', slug='bar', order=1)
+ page.save()
+
+ cell = LinkListCell.objects.create(order=0, placeholder='content', page=page)
+ item = WcsFormCell.objects.create(
+ page=page,
+ placeholder=cell.link_placeholder,
+ cached_title='A title',
+ cached_url='http://example.com',
+ cached_json={'foo': 'bar'},
+ order=0,
+ )
+
+ site_export = [x.get_serialized_page() for x in Page.objects.all()]
+ Page.objects.all().delete()
+
+ Page.load_serialized_pages(site_export)
+
+ new_page = Page.objects.get()
+ new_cells = CellBase.get_cells(page_id=new_page.id, placeholder='content')
+ assert len(new_cells[0].get_items()) == 1
+ new_item = new_cells[0].get_items()[0]
+ assert isinstance(new_item, WcsFormCell)
+ assert new_item.cached_title == item.cached_title
+ assert new_item.cached_url == item.cached_url
+ assert new_item.cached_json == item.cached_json