Projet

Général

Profil

0004-kb-add-cell-to-display-last-updated-pages-39091.patch

Nicolas Roche, 27 janvier 2020 17:55

Télécharger (12,8 ko)

Voir les différences:

Subject: [PATCH 4/4] kb: add cell to display last updated pages (#39091)

 combo/apps/kb/__init__.py                     |  26 ++++
 combo/apps/kb/migrations/0001_initial.py      |  39 +++++
 combo/apps/kb/migrations/__init__.py          |   0
 combo/apps/kb/models.py                       |  46 ++++++
 .../combo/last-pages-updates-cell.html        |  24 +++
 combo/settings.py                             |   1 +
 tests/test_kb.py                              | 137 ++++++++++++++++++
 7 files changed, 273 insertions(+)
 create mode 100644 combo/apps/kb/__init__.py
 create mode 100644 combo/apps/kb/migrations/0001_initial.py
 create mode 100644 combo/apps/kb/migrations/__init__.py
 create mode 100644 combo/apps/kb/models.py
 create mode 100644 combo/apps/kb/templates/combo/last-pages-updates-cell.html
 create mode 100644 tests/test_kb.py
combo/apps/kb/__init__.py
1
# combo - content management system
2
# Copyright (C) 2020  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
from django.utils.translation import ugettext_lazy as _
19

  
20

  
21
class AppConfig(django.apps.AppConfig):
22
    name = 'combo.apps.kb'
23
    verbose_name = _('Knowledge base')
24

  
25

  
26
default_app_config = 'combo.apps.kb.AppConfig'
combo/apps/kb/migrations/0001_initial.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-01-27 12:34
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6
import django.db.models.deletion
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    initial = True
12

  
13
    dependencies = [
14
        ('auth', '0008_alter_user_username_max_length'),
15
        ('data', '0041_page_creation_timestamp'),
16
    ]
17

  
18
    operations = [
19
        migrations.CreateModel(
20
            name='LastPagesUpdatesCell',
21
            fields=[
22
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23
                ('placeholder', models.CharField(max_length=20)),
24
                ('order', models.PositiveIntegerField()),
25
                ('slug', models.SlugField(blank=True, verbose_name='Slug')),
26
                ('extra_css_class', models.CharField(blank=True, max_length=100, verbose_name='Extra classes for CSS styling')),
27
                ('public', models.BooleanField(default=True, verbose_name='Public')),
28
                ('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
29
                ('last_update_timestamp', models.DateTimeField(auto_now=True)),
30
                ('limit', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Maximum number of entries')),
31
                ('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='Groups')),
32
                ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='data.Page')),
33
                ('root_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='kb_last_pages_updates_root_page', to='data.Page', verbose_name='Root Page')),
34
            ],
35
            options={
36
                'verbose_name': 'Last pages updates',
37
            },
38
        ),
39
    ]
combo/apps/kb/models.py
1
# combo - content management system
2
# Copyright (C) 2020  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 datetime
18

  
19
from django.db import models
20
from django.utils import timezone
21
from django.utils.translation import ugettext_lazy as _
22

  
23
from combo.data.models import CellBase, Page
24
from combo.data.library import register_cell_class
25

  
26
@register_cell_class
27
class LastPagesUpdatesCell(CellBase):
28
    root_page = models.ForeignKey(
29
        Page, on_delete=models.SET_NULL, null=True, blank=True,
30
        verbose_name=_('Root Page'), related_name='kb_last_pages_updates_root_page')
31
    limit = models.PositiveSmallIntegerField(
32
        _('Maximum number of entries'), null=True, blank=True)
33

  
34
    template_name = 'combo/last-pages-updates-cell.html'
35

  
36
    class Meta:
37
        verbose_name = _('Last pages updates')
38

  
39
    def get_cell_extra_context(self, context):
40
        extra_context = super(LastPagesUpdatesCell, self).get_cell_extra_context(context)
41
        if self.root_page:
42
            pages = self.root_page.get_descendants_and_me()
43
        else:
44
            pages = Page.objects.all()
45
        extra_context['pages'] = pages.order_by('-last_update_timestamp')[:self.limit]
46
        return extra_context
combo/apps/kb/templates/combo/last-pages-updates-cell.html
1
{% load i18n %}
2
{% block cell-content %}
3
<h2>{% trans "Last updated pages" %}</h2>
4
<div class="links-list">
5
  <ul class="lastpagesupdatescell--list">
6
    {% for page in pages %}
7
    <li class="lastpagesupdatescell--item">
8
      <a href="{{ page.url }}">
9
        <span class="lastpagesupdatescell--item-title">{{ page.title }}</span>
10
        <time class="lastpagesupdatescell--item-date"
11
              datetime={{ page.last_update_timestamp|datetime|date:"c"}}>
12
          {% trans "on"%} {{ page.last_update_timestamp }}
13
        </time>
14
        {% if page.is_new %}
15
        <span class="lastpagesupdatescell--item-isnew">
16
          {% trans "(new page)"%}
17
        </span>
18
        {% endif %}
19
      </a>
20
    </li>
21
    {% endfor %}
22
  </ul>
23
</div>
24
{% endblock %}
combo/settings.py
76 76
    'combo.apps.calendar',
77 77
    'combo.apps.pwa',
78 78
    'combo.apps.gallery',
79
    'combo.apps.kb',
79 80
    'haystack',
80 81
    'xstatic.pkg.josefinsans',
81 82
    'xstatic.pkg.leaflet',
tests/test_kb.py
1
# combo - content management system
2
# Copyright (C) 2020  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/>.import pytest
16

  
17
import pytest
18
import re
19

  
20
from combo.data.models import CellBase, Page, TextCell
21
from combo.apps.kb.models import LastPagesUpdatesCell
22

  
23
pytestmark = pytest.mark.django_db
24

  
25

  
26
def login(app, username='admin', password='admin'):
27
    login_page = app.get('/login/')
28
    login_form = login_page.forms[0]
29
    login_form['username'] = username
30
    login_form['password'] = password
31
    resp = login_form.submit()
32
    assert resp.status_int == 302
33
    return app
34

  
35

  
36
@pytest.mark.freeze_time('2020-01-01')
37
def test_manage_last_updated_content_cell(app, admin_user):
38
    page = Page(title='example page', slug='example-page')
39
    page.save()
40
    app = login(app)
41
    resp = app.get('/manage/pages/%s/' % page.id, status=200)
42

  
43
    optgroup = resp.html.find('optgroup', attrs={'label': 'Knowledge base'})
44
    add_cell_url = optgroup.findChild().attrs['data-add-url']
45
    assert 'kb_lastpagesupdatescell' in add_cell_url
46
    resp = app.get(add_cell_url, status=302)
47
    resp = resp.follow()
48
    cells = CellBase.get_cells(page_id=page.id)
49
    assert ('data-cell-reference="%s"' % cells[0].get_reference()) in resp.text
50
    resp = resp.forms[0].submit()
51
    assert resp.status_int == 302
52

  
53
    resp = app.get('/example-page/', status=200)
54
    div = resp.html.find('div', attrs={'class': 'lastpagesupdatescell'})
55
    assert 'on Jan. 1, 2020' in div.findChild('time').text
56

  
57
def test_last_updated_content_cell_new_page(freezer):
58
    freezer.move_to('2020-01-01')
59
    page = Page(title='example page', slug='example-page')
60
    page.save()
61
    cell = LastPagesUpdatesCell(page=page, order=0)
62
    cell.save()
63
    ctx = {}
64
    assert 'Jan. 1, 2020' in cell.render(ctx)
65
    assert page.snapshot is None
66
    assert '(new page)' in cell.render(ctx)
67

  
68
    freezer.move_to('2020-01-08')
69
    assert '(new page)' not in cell.render(ctx)
70

  
71
def test_last_updated_content_cell_limit(freezer):
72
    for i in range(1, 4):
73
        page = Page(title='page %s' %i, slug='page-%i' %i)
74
        page.save()
75
    cell = LastPagesUpdatesCell(page=page, order = 0)
76
    cell.save()
77
    ctx = {}
78
    assert cell.render(ctx).count('<li') == 3
79

  
80
    cell.limit = 2
81
    cell.save()
82
    assert cell.render(ctx).count('<li') == 2
83

  
84
def test_last_updated_content_cell_sort(freezer):
85
    for i in [30, 11, 2, 22]:
86
        freezer.move_to('2020-01-%02i' %i)
87
        page = Page(title='page %s' %i, slug='page-%i' %i)
88
        page.save()
89
        cell = TextCell(page=page, order=0, slug='cell-%i' %i, text='foo')
90
        cell.save()
91
    cell = LastPagesUpdatesCell(page=page, order=0, limit=3)
92
    cell.save()
93
    ctx = {}
94
    assert len(re.findall('<time', cell.render(ctx))) == 3
95
    times = re.finditer('<time[^>]*>([^<]*)</time>', cell.render(ctx))
96
    assert 'on Jan. 30, 2020' in next(times).group(0)
97
    assert 'on Jan. 22, 2020' in next(times).group(0)
98
    assert 'on Jan. 11, 2020' in next(times).group(0)
99

  
100
    # update contained cell
101
    freezer.move_to('2020-01-31')
102
    text_cell = TextCell.objects.get(slug='cell-2')
103
    text_cell.text = 'bar'
104
    text_cell.save()
105
    assert len(re.findall('<time', cell.render(ctx))) == 3
106
    times = re.finditer('<time[^>]*>([^<]*)</time>', cell.render(ctx))
107
    assert 'on Jan. 31, 2020' in next(times).group(0)
108
    assert 'on Jan. 30, 2020' in next(times).group(0)
109
    assert 'on Jan. 22, 2020' in next(times).group(0)
110

  
111
def test_last_updated_content_cell_root_page():
112
    ''' 1  3
113
        |
114
        2
115
    '''
116
    page1 = Page(title='page-1', slug='page-1')
117
    page1.save()
118
    page2 = Page(title='page-2', slug='page-2')
119
    page2.parent_id = page1.id
120
    page2.save()
121
    page3 = Page(title='page-3', slug='page-3')
122
    page3.save()
123
    cell = LastPagesUpdatesCell(slug='me')
124
    cell.page = page3
125
    cell.order = 0
126
    cell.save()
127
    ctx = {}
128
    assert cell.render(ctx).count('<li') == 3
129

  
130
    cell.root_page = Page.objects.get(slug='page-1')
131
    assert cell.render(ctx).count('<li') == 2
132
    cell.root_page = Page.objects.get(slug='page-2')
133
    assert cell.render(ctx).count('<li') == 1
134

  
135
    Page.objects.get(slug='page-2').delete()
136
    cell = CellBase.get_cells(slug='me')[0]  # reload cell
137
    assert cell.render(ctx).count('<li') == 2
0
-