general: add pre-configured json cells (#15723)
This commit is contained in:
parent
ddcbc0aef6
commit
b7d62e0021
|
@ -14,9 +14,11 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from .models import Page, ParametersCell, MenuCell
|
from .models import Page, ParametersCell, MenuCell, ConfigJsonCell
|
||||||
from jsonfield.widgets import JSONWidget
|
from jsonfield.widgets import JSONWidget
|
||||||
|
|
||||||
class ParametersForm(forms.Form):
|
class ParametersForm(forms.Form):
|
||||||
|
@ -61,3 +63,29 @@ class MenuCellForm(forms.ModelForm):
|
||||||
pages = Page.get_as_reordered_flat_hierarchy(Page.objects.all())
|
pages = Page.get_as_reordered_flat_hierarchy(Page.objects.all())
|
||||||
choices = [(x.id, '%s %s' % (u'\u00a0' * x.level * 2, x.title)) for x in pages]
|
choices = [(x.id, '%s %s' % (u'\u00a0' * x.level * 2, x.title)) for x in pages]
|
||||||
self.fields['root_page'].widget = forms.Select(choices=choices)
|
self.fields['root_page'].widget = forms.Select(choices=choices)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigJsonForm(forms.ModelForm):
|
||||||
|
formdef = []
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConfigJsonCell
|
||||||
|
fields = ('parameters',)
|
||||||
|
widgets = {'parameters': forms.HiddenInput()}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
parameters = copy.copy(kwargs['instance'].parameters or {})
|
||||||
|
# reset parameters in instance as the value is actually created from
|
||||||
|
# additional fields.
|
||||||
|
kwargs['instance'].parameters = None
|
||||||
|
super(ConfigJsonForm, self).__init__(*args, **kwargs)
|
||||||
|
for field in self.formdef:
|
||||||
|
self.fields[field['varname']] = forms.CharField(
|
||||||
|
label=field['label'],
|
||||||
|
initial=parameters.get(field['varname']))
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
self.cleaned_data['parameters'] = {}
|
||||||
|
for field in self.formdef:
|
||||||
|
self.cleaned_data['parameters'][field['varname']] = self.cleaned_data[field['varname']]
|
||||||
|
return self.cleaned_data
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import jsonfield.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0006_require_contenttypes_0002'),
|
||||||
|
('data', '0023_auto_20170313_1541'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ConfigJsonCell',
|
||||||
|
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)),
|
||||||
|
('extra_css_class', models.CharField(max_length=100, verbose_name='Extra classes for CSS styling', blank=True)),
|
||||||
|
('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)),
|
||||||
|
('key', models.CharField(max_length=50)),
|
||||||
|
('parameters', jsonfield.fields.JSONField(default=dict, blank=True)),
|
||||||
|
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
|
||||||
|
('page', models.ForeignKey(to='data.Page')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -804,24 +804,22 @@ class ParentContentCell(CellBase):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@register_cell_class
|
|
||||||
class JsonCell(CellBase):
|
class JsonCellBase(CellBase):
|
||||||
title = models.CharField(_('Title'), max_length=150, blank=True)
|
url = None
|
||||||
url = models.URLField(_('URL'), blank=True)
|
cache_duration = 60
|
||||||
template_string = models.TextField(_('Template'), blank=True, null=True)
|
template_string = None
|
||||||
cache_duration = models.PositiveIntegerField(
|
|
||||||
_('Cache duration'), default=60)
|
|
||||||
|
|
||||||
_json_content = None
|
_json_content = None
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('JSON Feed')
|
abstract = True
|
||||||
|
|
||||||
def is_visible(self, user=None):
|
def is_visible(self, user=None):
|
||||||
return bool(self.url) and super(JsonCell, self).is_visible(user=user)
|
return bool(self.url) and super(JsonCellBase, self).is_visible(user=user)
|
||||||
|
|
||||||
def get_cell_extra_context(self, context):
|
def get_cell_extra_context(self, context):
|
||||||
extra_context = super(JsonCell, self).get_cell_extra_context(context)
|
extra_context = super(JsonCellBase, self).get_cell_extra_context(context)
|
||||||
self._json_content = None
|
self._json_content = None
|
||||||
try:
|
try:
|
||||||
url = utils.get_templated_url(self.url, context)
|
url = utils.get_templated_url(self.url, context)
|
||||||
|
@ -862,4 +860,65 @@ class JsonCell(CellBase):
|
||||||
if self.template_string:
|
if self.template_string:
|
||||||
tmpl = Template(self.template_string)
|
tmpl = Template(self.template_string)
|
||||||
return tmpl.render(context)
|
return tmpl.render(context)
|
||||||
return super(JsonCell, self).render(context)
|
return super(JsonCellBase, self).render(context)
|
||||||
|
|
||||||
|
|
||||||
|
@register_cell_class
|
||||||
|
class JsonCell(JsonCellBase):
|
||||||
|
title = models.CharField(_('Title'), max_length=150, blank=True)
|
||||||
|
url = models.URLField(_('URL'), blank=True)
|
||||||
|
template_string = models.TextField(_('Template'), blank=True, null=True)
|
||||||
|
cache_duration = models.PositiveIntegerField(
|
||||||
|
_('Cache duration'), default=60)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('JSON Feed')
|
||||||
|
|
||||||
|
|
||||||
|
@register_cell_class
|
||||||
|
class ConfigJsonCell(JsonCellBase):
|
||||||
|
key = models.CharField(max_length=50)
|
||||||
|
parameters = JSONField(blank=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_cell_types(cls):
|
||||||
|
l = []
|
||||||
|
for key, definition in settings.JSON_CELL_TYPES.items():
|
||||||
|
l.append({
|
||||||
|
'name': definition['name'],
|
||||||
|
'variant': key,
|
||||||
|
'group': _('Extra'),
|
||||||
|
'cell_type_str': cls.get_cell_type_str(),
|
||||||
|
})
|
||||||
|
l.sort(lambda x, y: cmp(x.get('name'), y.get('name')))
|
||||||
|
return l
|
||||||
|
|
||||||
|
def get_label(self):
|
||||||
|
return settings.JSON_CELL_TYPES[self.key]['name']
|
||||||
|
|
||||||
|
def set_variant(self, variant):
|
||||||
|
self.key = variant
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url(self):
|
||||||
|
return settings.JSON_CELL_TYPES[self.key]['url']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template_name(self):
|
||||||
|
return 'combo/json/%s.html' % self.key
|
||||||
|
|
||||||
|
def get_default_form_class(self):
|
||||||
|
formdef = settings.JSON_CELL_TYPES[self.key].get('form')
|
||||||
|
if not formdef:
|
||||||
|
return None
|
||||||
|
from .forms import ConfigJsonForm
|
||||||
|
# create a subclass of ConfigJsonForm with 'formdef' (the list of
|
||||||
|
# fields) as an attribute.
|
||||||
|
config_form_class = type(str('%sConfigClass' % self.key),
|
||||||
|
(ConfigJsonForm,), {'formdef': formdef})
|
||||||
|
return config_form_class
|
||||||
|
|
||||||
|
def get_cell_extra_context(self, context):
|
||||||
|
ctx = super(ConfigJsonCell, self).get_cell_extra_context(context)
|
||||||
|
ctx['parameters'] = self.parameters
|
||||||
|
return ctx
|
||||||
|
|
|
@ -264,6 +264,8 @@ MELLON_IDENTITY_PROVIDERS = []
|
||||||
# mapping of payment modes
|
# mapping of payment modes
|
||||||
LINGO_NO_ONLINE_PAYMENT_REASONS = {}
|
LINGO_NO_ONLINE_PAYMENT_REASONS = {}
|
||||||
|
|
||||||
|
JSON_CELL_TYPES = {}
|
||||||
|
|
||||||
|
|
||||||
local_settings_file = os.environ.get('COMBO_SETTINGS_FILE',
|
local_settings_file = os.environ.get('COMBO_SETTINGS_FILE',
|
||||||
os.path.join(os.path.dirname(__file__), 'local_settings.py'))
|
os.path.join(os.path.dirname(__file__), 'local_settings.py'))
|
||||||
|
|
|
@ -4,7 +4,7 @@ import os
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from combo.data.models import Page, CellBase, TextCell, LinkCell, JsonCell
|
from combo.data.models import Page, CellBase, TextCell, LinkCell, JsonCell, ConfigJsonCell
|
||||||
from django.forms.widgets import Media
|
from django.forms.widgets import Media
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
@ -210,3 +210,23 @@ def test_json_cell():
|
||||||
assert requests_get.call_count == 1
|
assert requests_get.call_count == 1
|
||||||
assert requests_get.call_args[0][0] == 'http://testuser?email=foo%40example.net'
|
assert requests_get.call_args[0][0] == 'http://testuser?email=foo%40example.net'
|
||||||
assert requests_get.call_args[1]['cache_duration'] == 10
|
assert requests_get.call_args[1]['cache_duration'] == 10
|
||||||
|
|
||||||
|
def test_config_json_cell():
|
||||||
|
page = Page(title='example page', slug='example-page')
|
||||||
|
page.save()
|
||||||
|
|
||||||
|
request = RequestFactory().get('/')
|
||||||
|
|
||||||
|
with override_settings(JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/'}}):
|
||||||
|
cell = ConfigJsonCell()
|
||||||
|
cell.key = 'foobar'
|
||||||
|
cell.parameters = {'blah': 'plop'}
|
||||||
|
assert cell.get_label() == 'Foobar'
|
||||||
|
assert cell.url == 'http://test/'
|
||||||
|
assert cell.template_name == 'combo/json/foobar.html'
|
||||||
|
|
||||||
|
with mock.patch('combo.utils.requests.get') as requests_get:
|
||||||
|
requests_get.return_value = mock.Mock(content=json.dumps({'hello': 'world'}), status_code=200)
|
||||||
|
context = cell.get_cell_extra_context(Context({'request': request}))
|
||||||
|
assert context['json'] == {'hello': 'world'}
|
||||||
|
assert context['parameters'] == {'blah': 'plop'}
|
||||||
|
|
|
@ -6,13 +6,14 @@ import urllib
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import override_settings
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from webtest import TestApp
|
from webtest import TestApp
|
||||||
from webtest import Upload
|
from webtest import Upload
|
||||||
|
|
||||||
from combo.wsgi import application
|
from combo.wsgi import application
|
||||||
from combo.data.models import Page, CellBase, TextCell, LinkCell
|
from combo.data.models import Page, CellBase, TextCell, LinkCell, ConfigJsonCell
|
||||||
from combo.apps.search.models import SearchCell
|
from combo.apps.search.models import SearchCell
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
@ -423,6 +424,68 @@ def test_edit_cell_order(app, admin_user):
|
||||||
for i, cell in enumerate(cells):
|
for i, cell in enumerate(cells):
|
||||||
assert TextCell.objects.get(id=cell.id).order == new_order[i]
|
assert TextCell.objects.get(id=cell.id).order == new_order[i]
|
||||||
|
|
||||||
|
|
||||||
|
def test_edit_config_json_cell(app, admin_user):
|
||||||
|
Page.objects.all().delete()
|
||||||
|
page = Page(title='One', slug='one', template_name='standard')
|
||||||
|
page.save()
|
||||||
|
|
||||||
|
app = login(app)
|
||||||
|
resp = app.get('/manage/pages/%s/' % page.id)
|
||||||
|
options = [x.text for x in resp.html.find_all('option')]
|
||||||
|
assert not 'Foobar' in options
|
||||||
|
|
||||||
|
with override_settings(JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/'}}):
|
||||||
|
resp = app.get('/manage/pages/%s/' % page.id)
|
||||||
|
options = [x.text for x in resp.html.find_all('option')]
|
||||||
|
assert 'Foobar' in options
|
||||||
|
data_add_url = [x for x in resp.html.find_all('option') if x.text == 'Foobar'][0].get('data-add-url')
|
||||||
|
resp = app.get(data_add_url)
|
||||||
|
assert resp.location == 'http://testserver/manage/pages/%s/' % page.id
|
||||||
|
|
||||||
|
cells = CellBase.get_cells(page_id=page.id)
|
||||||
|
assert len(cells) == 1
|
||||||
|
assert isinstance(cells[0], ConfigJsonCell)
|
||||||
|
assert cells[0].key == 'foobar'
|
||||||
|
|
||||||
|
resp = app.get('/manage/pages/%s/' % page.id)
|
||||||
|
assert ('data-cell-reference="%s"' % cells[0].get_reference()) in resp.body
|
||||||
|
assert 'There are no options for this cell.' in resp.form.text
|
||||||
|
|
||||||
|
# make it configurable
|
||||||
|
with override_settings(JSON_CELL_TYPES=
|
||||||
|
{'foobar': {
|
||||||
|
'name': 'Foobar',
|
||||||
|
'url': 'http://test/',
|
||||||
|
'form': [
|
||||||
|
{
|
||||||
|
'label': 'Test',
|
||||||
|
'type': 'string',
|
||||||
|
'varname': 'test',
|
||||||
|
}
|
||||||
|
]}}):
|
||||||
|
resp = app.get('/manage/pages/%s/' % page.id)
|
||||||
|
assert not 'There are no options for this cell.' in resp.form.text
|
||||||
|
|
||||||
|
resp.form['c%s-test' % cells[0].get_reference()].value = 'Hello world'
|
||||||
|
resp = resp.form.submit()
|
||||||
|
assert resp.status_int == 302
|
||||||
|
assert resp.location == 'http://testserver/manage/pages/%s/' % page.id
|
||||||
|
|
||||||
|
resp = app.get('/manage/pages/%s/' % page.id)
|
||||||
|
assert resp.form['c%s-test' % cells[0].get_reference()].value == 'Hello world'
|
||||||
|
|
||||||
|
resp = app.get('/manage/pages/%s/' % page.id)
|
||||||
|
assert ('data-cell-reference="%s"' % cells[0].get_reference()) in resp.body
|
||||||
|
resp.forms[0]['c%s-test' % cells[0].get_reference()].value = 'World Hello'
|
||||||
|
resp = resp.form.submit(xhr=True)
|
||||||
|
assert resp.status_int == 200
|
||||||
|
assert resp.body.startswith('<p><label')
|
||||||
|
|
||||||
|
resp = app.get('/manage/pages/%s/' % page.id)
|
||||||
|
assert resp.form['c%s-test' % cells[0].get_reference()].value == 'World Hello'
|
||||||
|
|
||||||
|
|
||||||
def test_logout(app, admin_user):
|
def test_logout(app, admin_user):
|
||||||
app = login(app)
|
app = login(app)
|
||||||
app.get('/logout/')
|
app.get('/logout/')
|
||||||
|
|
Loading…
Reference in New Issue