Projet

Général

Profil

0001-data-add-display-condition-to-cells-66263.patch

Lauréline Guérin, 16 juin 2022 11:46

Télécharger (51,3 ko)

Voir les différences:

Subject: [PATCH] data: add display condition to cells (#66263)

and also on each item of a linklist cell
 .../migrations/0004_display_condition.py      | 16 ++++
 combo/apps/calendar/models.py                 |  4 +-
 .../migrations/0004_display_condition.py      | 16 ++++
 combo/apps/dashboard/views.py                 |  2 +-
 .../migrations/0024_display_condition.py      | 31 +++++++
 combo/apps/dataviz/views.py                   |  2 +-
 .../migrations/0013_display_condition.py      | 16 ++++
 combo/apps/family/models.py                   |  6 +-
 .../migrations/0006_display_condition.py      | 16 ++++
 combo/apps/fargo/models.py                    |  6 +-
 .../migrations/0005_display_condition.py      | 21 +++++
 .../kb/migrations/0004_display_condition.py   | 16 ++++
 .../migrations/0046_display_condition.py      | 46 ++++++++++
 .../maps/migrations/0020_display_condition.py | 41 +++++++++
 combo/apps/maps/views.py                      |  2 +-
 .../migrations/0007_display_condition.py      | 26 ++++++
 combo/apps/notifications/models.py            |  6 +-
 .../migrations/0013_display_condition.py      | 16 ++++
 combo/apps/search/models.py                   |  6 +-
 .../wcs/migrations/0046_display_condition.py  | 68 +++++++++++++++
 combo/apps/wcs/models.py                      | 12 +--
 combo/data/forms.py                           | 30 ++++++-
 .../data/migrations/0056_display_condition.py | 66 ++++++++++++++
 combo/data/models.py                          | 33 +++++--
 combo/manager/forms.py                        | 21 +++++
 .../migrations/0004_display_condition.py      | 16 ++++
 combo/profile/models.py                       |  6 +-
 combo/public/templatetags/combo.py            |  2 +-
 combo/public/views.py                         |  6 +-
 combo/settings.py                             |  1 +
 tests/settings.py                             |  1 +
 tests/test_cells.py                           | 10 ++-
 tests/test_manager.py                         | 32 ++++++-
 tests/test_notification.py                    |  4 +-
 tests/test_public.py                          | 30 +++++++
 tests/test_search.py                          | 10 ++-
 tests/test_wcs.py                             | 87 +++++++++++++++++--
 37 files changed, 671 insertions(+), 59 deletions(-)
 create mode 100644 combo/apps/calendar/migrations/0004_display_condition.py
 create mode 100644 combo/apps/dashboard/migrations/0004_display_condition.py
 create mode 100644 combo/apps/dataviz/migrations/0024_display_condition.py
 create mode 100644 combo/apps/family/migrations/0013_display_condition.py
 create mode 100644 combo/apps/fargo/migrations/0006_display_condition.py
 create mode 100644 combo/apps/gallery/migrations/0005_display_condition.py
 create mode 100644 combo/apps/kb/migrations/0004_display_condition.py
 create mode 100644 combo/apps/lingo/migrations/0046_display_condition.py
 create mode 100644 combo/apps/maps/migrations/0020_display_condition.py
 create mode 100644 combo/apps/notifications/migrations/0007_display_condition.py
 create mode 100644 combo/apps/search/migrations/0013_display_condition.py
 create mode 100644 combo/apps/wcs/migrations/0046_display_condition.py
 create mode 100644 combo/data/migrations/0056_display_condition.py
 create mode 100644 combo/profile/migrations/0004_display_condition.py
combo/apps/calendar/migrations/0004_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('calendar', '0003_bookingcalendar_template_name'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='bookingcalendar',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
    ]
combo/apps/calendar/models.py
58 58
    def is_enabled(cls):
59 59
        return settings.BOOKING_CALENDAR_CELL_ENABLED and is_chrono_enabled() and is_wcs_enabled()
60 60

  
61
    def is_visible(self, **kwargs):
62
        return self.agenda_reference and self.formdef_reference and super().is_visible(**kwargs)
61
    def is_visible(self, request, **kwargs):
62
        return self.agenda_reference and self.formdef_reference and super().is_visible(request, **kwargs)
63 63

  
64 64
    def get_cell_extra_context(self, context):
65 65
        if context.get('placeholder_search_mode'):
combo/apps/dashboard/migrations/0004_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('dashboard', '0003_dashboardcell_template_name'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='dashboardcell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
    ]
combo/apps/dashboard/views.py
65 65
        cell = CellBase.get_cell(kwargs['cell_reference'])
66 66
        if not cell.page.is_visible(request.user):
67 67
            raise PermissionDenied()
68
        if not cell.is_visible(user=request.user):
68
        if not cell.is_visible(request):
69 69
            raise PermissionDenied()
70 70
        cell.pk = None
71 71
        cell.page = dashboard.page
combo/apps/dataviz/migrations/0024_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('dataviz', '0023_statistic_has_future_data'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='chartcell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
        migrations.AddField(
17
            model_name='chartfilterscell',
18
            name='condition',
19
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
20
        ),
21
        migrations.AddField(
22
            model_name='chartngcell',
23
            name='condition',
24
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
25
        ),
26
        migrations.AddField(
27
            model_name='gauge',
28
            name='condition',
29
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
30
        ),
31
    ]
combo/apps/dataviz/views.py
45 45

  
46 46
        if not self.cell.page.is_visible(request.user):
47 47
            raise PermissionDenied()
48
        if not self.cell.is_visible(user=request.user):
48
        if not self.cell.is_visible(request):
49 49
            raise PermissionDenied()
50 50
        if not self.cell.statistic or not self.cell.statistic.url:
51 51
            raise Http404('misconfigured cell')
combo/apps/family/migrations/0013_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('family', '0012_booking_form_url'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='weeklyagendacell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
    ]
combo/apps/family/models.py
94 94
            )
95 95
        )
96 96

  
97
    def is_visible(self, **kwargs):
98
        user = kwargs.get('user')
97
    def is_visible(self, request, **kwargs):
98
        user = getattr(request, 'user', None)
99 99
        if not user or user.is_anonymous:
100 100
            return False
101
        return super().is_visible(**kwargs)
101
        return super().is_visible(request, **kwargs)
102 102

  
103 103
    def get_cell_extra_context(self, context):
104 104
        if context.get('placeholder_search_mode'):
combo/apps/fargo/migrations/0006_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('fargo', '0005_recentdocumentscell_template_name'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='recentdocumentscell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
    ]
combo/apps/fargo/models.py
58 58
            self.__class__, fields=self.get_form_fields(), widgets=self.get_form_widgets()
59 59
        )
60 60

  
61
    def is_visible(self, **kwargs):
62
        user = kwargs.get('user')
61
    def is_visible(self, request, **kwargs):
62
        user = getattr(request, 'user', None)
63 63
        if not user or user.is_anonymous:
64 64
            return False
65
        return super().is_visible(**kwargs)
65
        return super().is_visible(request, **kwargs)
66 66

  
67 67
    @classmethod
68 68
    def is_enabled(cls):
combo/apps/gallery/migrations/0005_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('gallery', '0004_auto_20210723_1318'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='gallerycell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
        migrations.AlterField(
17
            model_name='image',
18
            name='image',
19
            field=models.ImageField(upload_to='uploads/gallery/%Y/%m/', verbose_name='Image'),
20
        ),
21
    ]
combo/apps/kb/migrations/0004_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('kb', '0003_latestpageupdatescell_template_name'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='latestpageupdatescell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
    ]
combo/apps/lingo/migrations/0046_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('lingo', '0045_basketitem_remote_item_id'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='activeitems',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
        migrations.AddField(
17
            model_name='itemshistory',
18
            name='condition',
19
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
20
        ),
21
        migrations.AddField(
22
            model_name='lingobasketcell',
23
            name='condition',
24
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
25
        ),
26
        migrations.AddField(
27
            model_name='lingobasketlinkcell',
28
            name='condition',
29
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
30
        ),
31
        migrations.AddField(
32
            model_name='lingorecenttransactionscell',
33
            name='condition',
34
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
35
        ),
36
        migrations.AddField(
37
            model_name='selfdeclaredinvoicepayment',
38
            name='condition',
39
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
40
        ),
41
        migrations.AddField(
42
            model_name='tipipaymentformcell',
43
            name='condition',
44
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
45
        ),
46
    ]
combo/apps/maps/migrations/0020_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('maps', '0019_auto_20211104_1603'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='map',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
        migrations.AlterField(
17
            model_name='maplayer',
18
            name='tiles_attribution',
19
            field=models.CharField(
20
                blank=True,
21
                help_text=(
22
                    'Par exemple\xa0: Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors, '
23
                    '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
24
                ),
25
                max_length=1024,
26
                null=True,
27
                verbose_name='Attribution',
28
            ),
29
        ),
30
        migrations.AlterField(
31
            model_name='maplayer',
32
            name='tiles_template_url',
33
            field=models.CharField(
34
                blank=True,
35
                help_text='Par exemple\xa0: https://tiles.entrouvert.org/hdm/{z}/{x}/{y}.png',
36
                max_length=1024,
37
                null=True,
38
                verbose_name='Tiles URL',
39
            ),
40
        ),
41
    ]
combo/apps/maps/views.py
27 27
    def get(self, request, *args, **kwargs):
28 28
        cell = get_object_or_404(Map, pk=kwargs['cell_id'])
29 29
        layer = get_object_or_404(cell.layers.all(), kind='geojson', slug=kwargs['layer_slug'])
30
        if not cell.page.is_visible(request.user) or not cell.is_visible(user=request.user):
30
        if not cell.page.is_visible(request.user) or not cell.is_visible(request):
31 31
            return HttpResponseForbidden()
32 32
        options = cell.maplayeroptions_set.get(map_layer=layer)
33 33
        geojson = layer.get_geojson(request, options.properties)
combo/apps/notifications/migrations/0007_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('notifications', '0006_auto_20210723_1318'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='notificationscell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
        migrations.AlterField(
17
            model_name='notification',
18
            name='body',
19
            field=models.TextField(blank=True, default='', verbose_name='Body'),
20
        ),
21
        migrations.AlterField(
22
            model_name='notification',
23
            name='url',
24
            field=models.URLField(blank=True, default='', verbose_name='URL'),
25
        ),
26
    ]
combo/apps/notifications/models.py
176 176
    class Meta:
177 177
        verbose_name = _('User Notifications')
178 178

  
179
    def is_visible(self, **kwargs):
180
        user = kwargs.get('user')
179
    def is_visible(self, request, **kwargs):
180
        user = getattr(request, 'user', None)
181 181
        if user is None or not user.is_authenticated:
182 182
            return False
183
        return super().is_visible(**kwargs)
183
        return super().is_visible(request, **kwargs)
184 184

  
185 185
    def get_cell_extra_context(self, context):
186 186
        extra_context = super().get_cell_extra_context(context)
combo/apps/search/migrations/0013_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('search', '0012_auto_20220105_0832'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='searchcell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
    ]
combo/apps/search/models.py
60 60
    class Meta:
61 61
        verbose_name = _('Search')
62 62

  
63
    def is_visible(self, **kwargs):
63
    def is_visible(self, request, **kwargs):
64 64
        if not self._search_services.get('data'):
65 65
            return False
66
        return super().is_visible(**kwargs)
66
        return super().is_visible(request, **kwargs)
67 67

  
68 68
    def get_default_form_class(self):
69 69
        from .forms import SearchCellForm
......
176 176
    @classmethod
177 177
    def ajax_results_view(cls, request, cell_pk, service_slug):
178 178
        cell = get_object_or_404(cls, pk=cell_pk)
179
        if not cell.is_visible(user=request.user) or not cell.page.is_visible(request.user):
179
        if not cell.is_visible(request) or not cell.page.is_visible(request.user):
180 180
            raise PermissionDenied
181 181

  
182 182
        query = request.GET.get('q')
combo/apps/wcs/migrations/0046_display_condition.py
1
# Generated by Django 2.2.26 on 2022-06-15 05:51
2

  
3
from django.db import migrations, models
4

  
5

  
6
class Migration(migrations.Migration):
7

  
8
    dependencies = [
9
        ('wcs', '0045_wcscareformscell_cache_duration'),
10
    ]
11

  
12
    operations = [
13
        migrations.AddField(
14
            model_name='backofficesubmissioncell',
15
            name='condition',
16
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
17
        ),
18
        migrations.AddField(
19
            model_name='categoriescell',
20
            name='condition',
21
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
22
        ),
23
        migrations.AddField(
24
            model_name='trackingcodeinputcell',
25
            name='condition',
26
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
27
        ),
28
        migrations.AddField(
29
            model_name='wcscardinfoscell',
30
            name='condition',
31
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
32
        ),
33
        migrations.AddField(
34
            model_name='wcscardscell',
35
            name='condition',
36
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
37
        ),
38
        migrations.AddField(
39
            model_name='wcscareformscell',
40
            name='condition',
41
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
42
        ),
43
        migrations.AddField(
44
            model_name='wcscategorycell',
45
            name='condition',
46
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
47
        ),
48
        migrations.AddField(
49
            model_name='wcscurrentdraftscell',
50
            name='condition',
51
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
52
        ),
53
        migrations.AddField(
54
            model_name='wcscurrentformscell',
55
            name='condition',
56
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
57
        ),
58
        migrations.AddField(
59
            model_name='wcsformcell',
60
            name='condition',
61
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
62
        ),
63
        migrations.AddField(
64
            model_name='wcsformsofcategorycell',
65
            name='condition',
66
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
67
        ),
68
    ]
combo/apps/wcs/models.py
414 414
    class Meta:
415 415
        abstract = True
416 416

  
417
    def is_visible(self, **kwargs):
418
        user = kwargs.get('user')
417
    def is_visible(self, request, **kwargs):
418
        user = getattr(request, 'user', None)
419 419
        if not user or user.is_anonymous:
420 420
            return False
421
        return super().is_visible(**kwargs)
421
        return super().is_visible(request, **kwargs)
422 422

  
423 423

  
424 424
class CategoriesAndWcsSiteValidityMixin:
......
886 886

  
887 887
        populate_cache()
888 888

  
889
    def is_visible(self, **kwargs):
890
        user = kwargs.get('user')
889
    def is_visible(self, request, **kwargs):
890
        user = getattr(request, 'user', None)
891 891
        if self.only_for_user and (not user or user.is_anonymous):
892 892
            return False
893
        return super().is_visible(**kwargs)
893
        return super().is_visible(request, **kwargs)
894 894

  
895 895
    def get_api_url(self, context):
896 896
        parts = self.carddef_reference.split(':')
combo/data/forms.py
17 17
import copy
18 18

  
19 19
from django import forms
20
from django.conf import settings
21
from django.template import Template, TemplateSyntaxError
22
from django.utils.translation import ugettext_lazy as _
20 23

  
21 24
from combo.utils import cache_during_request
22 25

  
......
52 55
class LinkCellForLinkListCellForm(LinkCellForm):
53 56
    class Meta:
54 57
        model = LinkCell
55
        fields = ('title', 'url', 'link_page', 'anchor', 'bypass_url_validity_check', 'extra_css_class')
58
        fields = (
59
            'title',
60
            'url',
61
            'link_page',
62
            'anchor',
63
            'bypass_url_validity_check',
64
            'extra_css_class',
65
            'condition',
66
        )
67

  
68
    def __init__(self, *args, **kwargs):
69
        super().__init__(*args, **kwargs)
70
        if not settings.CELL_CONDITIONS_ENABLED:
71
            del self.fields['condition']
72

  
73
    def clean_condition(self):
74
        condition = self.cleaned_data['condition']
75
        try:
76
            Template('{%% if %s %%}OK{%% endif %%}' % condition)
77
        except TemplateSyntaxError:
78
            raise forms.ValidationError(_('Invalid syntax.'))
79

  
80
        return condition
56 81

  
57 82

  
58 83
class LinkListCellForm(forms.ModelForm):
......
60 85
        model = LinkListCell
61 86
        fields = ['limit']
62 87

  
63
    def __init__(self, *args, **kwargs):
64
        super().__init__(*args, **kwargs)
65

  
66 88

  
67 89
class ConfigJsonForm(forms.ModelForm):
68 90
    formdef = []
combo/data/migrations/0056_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('data', '0055_page_placeholer_options'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='configjsoncell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
        migrations.AddField(
17
            model_name='feedcell',
18
            name='condition',
19
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
20
        ),
21
        migrations.AddField(
22
            model_name='fortunecell',
23
            name='condition',
24
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
25
        ),
26
        migrations.AddField(
27
            model_name='jsoncell',
28
            name='condition',
29
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
30
        ),
31
        migrations.AddField(
32
            model_name='linkcell',
33
            name='condition',
34
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
35
        ),
36
        migrations.AddField(
37
            model_name='linklistcell',
38
            name='condition',
39
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
40
        ),
41
        migrations.AddField(
42
            model_name='menucell',
43
            name='condition',
44
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
45
        ),
46
        migrations.AddField(
47
            model_name='parentcontentcell',
48
            name='condition',
49
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
50
        ),
51
        migrations.AddField(
52
            model_name='textcell',
53
            name='condition',
54
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
55
        ),
56
        migrations.AddField(
57
            model_name='unlockmarkercell',
58
            name='condition',
59
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
60
        ),
61
        migrations.AlterField(
62
            model_name='linkcell',
63
            name='title',
64
            field=models.CharField(blank=True, max_length=150, verbose_name='Label'),
65
        ),
66
    ]
combo/data/models.py
806 806
    slug = models.SlugField(_('Slug'), blank=True)
807 807
    extra_css_class = models.CharField(_('Extra classes for CSS styling'), max_length=100, blank=True)
808 808
    template_name = models.CharField(_('Cell Template'), max_length=50, blank=True, null=True)
809
    condition = models.CharField(_('Display condition'), max_length=1000, blank=True, null=True)
809 810

  
810 811
    public = models.BooleanField(_('Public'), default=True)
811 812
    # restricted_to_unlogged is actually an invert switch, it is used for mark
......
1151 1152
                'last_update_timestamp',
1152 1153
                'restricted_to_unlogged',
1153 1154
                'template_name',
1155
                'condition',
1154 1156
            )
1155 1157
            + tuple(self.get_appearance_fields())
1156 1158
        ]
......
1325 1327
            and validity_info.invalid_datetime <= now()
1326 1328
        )
1327 1329

  
1328
    def is_visible(self, user=None, check_validity_info=True):
1330
    def compute_condition(self, request):
1331
        condition = self.condition
1332
        if not condition:
1333
            return True
1334
        context = RequestContext(request)
1335
        if condition in self.page.extra_variables:
1336
            condition = self.page.extra_variables[condition].strip('{ }')
1337
        try:
1338
            return Template('{%% if %s %%}OK{%% endif %%}' % condition).render(context) == 'OK'
1339
        except (TemplateSyntaxError, VariableDoesNotExist):
1340
            return False
1341

  
1342
    def is_visible(self, request, check_validity_info=True):
1343
        condition = self.compute_condition(request=request)
1344
        if not condition:
1345
            return False
1329 1346
        if check_validity_info and self.is_hidden_because_invalid():
1330 1347
            return False
1331
        return element_is_visible(self, user=user)
1348
        return element_is_visible(self, user=getattr(request, 'user', None))
1332 1349

  
1333 1350
    def is_relevant(self, context):
1334 1351
        """Return whether it's relevant to render this cell in the page
......
1842 1859
        for link in self.get_items():
1843 1860
            link.duplicate(page_target=new_cell.page, placeholder=new_cell.link_placeholder)
1844 1861

  
1845
    def is_visible(self, check_validity_info=True, **kwargs):
1862
    def is_visible(self, request, check_validity_info=True, **kwargs):
1846 1863
        # cell is visible even if items are invalid
1847
        return super().is_visible(check_validity_info=False, **kwargs)
1864
        return super().is_visible(request, check_validity_info=False, **kwargs)
1848 1865

  
1849 1866
    def check_validity(self):
1850 1867
        for link in self.get_items(prefetch_validity_info=True):
......
1880 1897
    class Meta:
1881 1898
        verbose_name = _('RSS/Atom Feed')
1882 1899

  
1883
    def is_visible(self, **kwargs):
1884
        return bool(self.url) and super().is_visible(**kwargs)
1900
    def is_visible(self, *args, **kwargs):
1901
        return bool(self.url) and super().is_visible(*args, **kwargs)
1885 1902

  
1886 1903
    def save(self, *args, **kwargs):
1887 1904
        result = super().save(*args, **kwargs)
......
2017 2034
            self.mark_as_valid()
2018 2035
        return result
2019 2036

  
2020
    def is_visible(self, **kwargs):
2021
        return bool(self.url) and super().is_visible(**kwargs)
2037
    def is_visible(self, *args, **kwargs):
2038
        return bool(self.url) and super().is_visible(*args, **kwargs)
2022 2039

  
2023 2040
    def is_user_dependant(self, context=None):
2024 2041
        urls = [self.url] + [x['url'] for x in self.additional_data or []]
combo/manager/forms.py
276 276
        ],
277 277
    )
278 278
    groups = forms.MultipleChoiceField(label=_('Groups'), required=False, choices=get_groups_as_choices)
279
    condition = forms.CharField(
280
        label=_('Display condition'),
281
        widget=forms.TextInput(attrs={'class': 'text-wide'}),
282
        max_length=1000,
283
        required=False,
284
    )
279 285

  
280 286
    def __init__(self, *args, **kwargs):
281 287
        self.instance = instance = kwargs.pop('instance')
......
293 299
                    initial['visibility'] = 'groups-none'
294 300
                else:
295 301
                    initial['visibility'] = 'groups-any'
302
        initial['condition'] = instance.condition
296 303
        super().__init__(*args, **kwargs)
304
        if not settings.CELL_CONDITIONS_ENABLED:
305
            del self.fields['condition']
306

  
307
    def clean_condition(self):
308
        condition = self.cleaned_data['condition']
309
        if not condition:
310
            return condition
311
        try:
312
            Template('{%% if %s %%}OK{%% endif %%}' % condition)
313
        except TemplateSyntaxError:
314
            raise ValidationError(_('Invalid syntax.'))
315

  
316
        return condition
297 317

  
298 318
    def save(self):
299 319
        if self.cleaned_data['visibility'] == 'all':
......
316 336
            self.instance.public = False
317 337
            self.instance.restricted_to_unlogged = True
318 338
            self.instance.groups.set(self.cleaned_data['groups'])
339
        self.instance.condition = self.cleaned_data.get('condition')
319 340
        self.instance.save()
320 341
        return self.instance
321 342

  
combo/profile/migrations/0004_display_condition.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('profile', '0003_profilecell_template_name'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='profilecell',
13
            name='condition',
14
            field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
15
        ),
16
    ]
combo/profile/models.py
45 45
        idp = list(settings.KNOWN_SERVICES.get('authentic').values())[0]
46 46
        return '%sapi/users/{{ concerned_user|name_id }}/' % idp.get('url')
47 47

  
48
    def is_visible(self, **kwargs):
49
        user = kwargs.get('user')
48
    def is_visible(self, request, **kwargs):
49
        user = getattr(request, 'user', None)
50 50
        if not user or user.is_anonymous:
51 51
            return False
52
        return super().is_visible(**kwargs)
52
        return super().is_visible(request, **kwargs)
53 53

  
54 54
    def get_cell_extra_context(self, context):
55 55
        extra_context = super().get_cell_extra_context(context)
combo/public/templatetags/combo.py
99 99
        and (
100 100
            context.get('render_skeleton')
101 101
            or x.is_relevant(context)
102
            and x.is_visible(user=context['request'].user, check_validity_info=False)
102
            and x.is_visible(context['request'], check_validity_info=False)
103 103
        )
104 104
    ]
105 105
    if context.get('render_skeleton'):
combo/public/views.py
129 129
    except ObjectDoesNotExist:
130 130
        raise Http404()
131 131

  
132
    if not cell.is_visible(user=request.user):
132
    if not cell.is_visible(request):
133 133
        raise PermissionDenied()
134 134

  
135 135
    exception = None
......
187 187
                    placeholder__in=['_auto_tile', '_dashboard', '_suggested_tile']
188 188
                )
189 189
            )
190
        other_cells = [x for x in other_cells if x.is_visible(user=request.user)]
190
        other_cells = [x for x in other_cells if x.is_visible(request)]
191 191
        other_cells.sort(key=lambda x: x.order)
192 192
        for other_cell in other_cells:
193 193
            if other_cell.get_reference() != cell.get_reference():
......
554 554
        cells_exclude=Q(placeholder__in=['_auto_tile', '_dashboard', '_suggested_tile']),
555 555
    )
556 556
    extend_with_parent_cells(cells, hierarchy=pages)
557
    cells = [x for x in cells if x.is_visible(user=request.user)]
557
    cells = [x for x in cells if x.is_visible(request)]
558 558
    mark_duplicated_slugs(cells)
559 559

  
560 560
    # load assets
combo/settings.py
367 367
BOOKING_CALENDAR_CELL_ENABLED = False
368 368
LEGACY_CHART_CELL_ENABLED = False
369 369
PUBLIK_FAMILY_CELL_ENABLED = False
370
CELL_CONDITIONS_ENABLED = False
370 371

  
371 372
# and enable others
372 373
CHART_FILTERS_CELL_ENABLED = True
tests/settings.py
90 90
LEGACY_CHART_CELL_ENABLED = True
91 91
PUBLIK_FAMILY_CELL_ENABLED = True
92 92
CHART_FILTERS_CELL_ENABLED = True
93
CELL_CONDITIONS_ENABLED = True
93 94

  
94 95
USER_PROFILE_CONFIG = {
95 96
    'fields': [
tests/test_cells.py
330 330
    assert validity_info.invalid_since is not None
331 331
    validity_info.invalid_since = now() - datetime.timedelta(days=2)
332 332
    validity_info.save()
333
    assert cell.is_visible()  # particular case: cell is visible
333
    request = RequestFactory().get('/')
334
    assert cell.is_visible(request)  # particular case: cell is visible
334 335

  
335 336

  
336 337
def test_feed_cell_validity(context):
......
1559 1560

  
1560 1561

  
1561 1562
def test_cell_is_visible():
1563
    request = RequestFactory().get('/')
1562 1564
    page = Page.objects.create()
1563 1565
    cell = TextCell.objects.create(page=page, order=0)
1564
    assert cell.is_visible() is True
1566
    assert cell.is_visible(request) is True
1565 1567

  
1566 1568
    # invalid cell since just now
1567 1569
    validity_info = ValidityInfo.objects.create(content_object=cell)
1568 1570
    validity_info.invalid_reason_code = 'FOO'
1569 1571
    validity_info.invalid_since = now()
1570 1572
    validity_info.save()
1571
    assert cell.is_visible() is True
1573
    assert cell.is_visible(request) is True
1572 1574

  
1573 1575
    # invalid cell since two days
1574 1576
    validity_info.invalid_since = now() - datetime.timedelta(days=2)
1575 1577
    validity_info.save()
1576
    assert cell.is_visible() is False
1578
    assert cell.is_visible(request) is False
1577 1579

  
1578 1580

  
1579 1581
def test_cell_invalidity_marker():
tests/test_manager.py
1491 1491
    assert PageSnapshot.objects.filter(page=other_page).count() == 2
1492 1492

  
1493 1493

  
1494
def test_edit_cell_visibility(app, admin_user):
1494
def test_edit_cell_visibility(settings, app, admin_user):
1495
    settings.CELL_CONDITIONS_ENABLED = False
1496

  
1495 1497
    Page.objects.all().delete()
1496 1498
    page = Page(title='One', slug='one', template_name='standard')
1497 1499
    page.save()
......
1501 1503
    app = login(app)
1502 1504
    resp = app.get('/manage/pages/%s/' % page.id)
1503 1505
    assert resp.form['cdata_textcell-%s-visibility' % cell.id].value == 'all'
1506
    assert 'cdata_textcell-%s-condition' % cell.id not in resp
1504 1507
    resp.form['cdata_textcell-%s-visibility' % cell.id] = 'logged'
1505 1508
    resp = resp.form.submit('submit')
1506 1509
    assert TextCell.objects.get(id=cell.id).public is False
......
1546 1549
    assert TextCell.objects.get(id=cell.id).groups.count() == 1
1547 1550
    assert TextCell.objects.get(id=cell.id).groups.all()[0].name == 'Another group'
1548 1551

  
1552
    settings.CELL_CONDITIONS_ENABLED = True
1553
    resp = app.get('/manage/pages/%s/' % page.id)
1554
    resp.form['cdata_textcell-%s-condition' % cell.pk] = 'False #'
1555
    resp = resp.form.submit('submit')
1556
    assert resp.json['errorlist']['visibility']['condition'] == ['Invalid syntax.']
1557

  
1558
    resp = app.get('/manage/pages/%s/' % page.id)
1559
    resp.form['cdata_textcell-%s-condition' % cell.pk] = 'False'
1560
    resp = resp.form.submit('submit')
1561
    assert TextCell.objects.get(id=cell.id).condition == 'False'
1562

  
1549 1563

  
1550 1564
def test_edit_cell_options(app, admin_user):
1551 1565
    Page.objects.all().delete()
......
2648 2662
    assert form.errors['url'] == ["syntax error: Could not parse the remainder: '{test_url' from '{test_url'"]
2649 2663

  
2650 2664

  
2651
def test_add_edit_delete_list_link_item(app, admin_user):
2665
def test_add_edit_delete_list_link_item(settings, app, admin_user):
2666
    settings.CELL_CONDITIONS_ENABLED = False
2652 2667
    Page.objects.all().delete()
2653 2668
    page = Page.objects.create(title='One', slug='one', template_name='standard')
2654 2669
    cell = LinkListCell.objects.create(order=0, placeholder='content', page=page)
......
2657 2672
    assert PageSnapshot.objects.count() == 0
2658 2673

  
2659 2674
    resp = resp.click(href='.*/add-link/link$')
2675
    assert 'condition' not in resp.context['form'].fields
2660 2676
    resp.forms[0]['title'] = 'Hello world'
2661 2677
    resp.forms[0]['url'] = 'http://example.com'
2662 2678
    resp.forms[0]['extra_css_class'] = 'foobar'
......
2693 2709
    assert LinkCell.objects.count() == 0
2694 2710
    assert PageSnapshot.objects.count() == 3
2695 2711

  
2712
    settings.CELL_CONDITIONS_ENABLED = True
2713
    resp = resp.follow()
2714
    resp = resp.click(href='.*/add-link/link$')
2715
    resp.forms[0]['condition'] = 'False #'
2716
    resp = resp.forms[0].submit()
2717
    assert resp.context['form'].errors['condition'] == ['Invalid syntax.']
2718

  
2719
    resp.forms[0]['condition'] = 'True'
2720
    resp = resp.forms[0].submit()
2721
    item = LinkCell.objects.get()
2722
    assert item.condition == 'True'
2723

  
2696 2724

  
2697 2725
def test_edit_link_list_order(app, admin_user):
2698 2726
    Page.objects.all().delete()
tests/test_notification.py
93 93
    context['synchronous'] = True  # to get fresh content
94 94

  
95 95
    context['request'].user = None
96
    assert cell.is_visible(user=context['request'].user) is False
96
    assert cell.is_visible(context['request']) is False
97 97
    context['request'].user = john_doe
98
    assert cell.is_visible(user=context['request'].user) is True
98
    assert cell.is_visible(context['request']) is True
99 99
    assert cell.get_badge(context) is None
100 100

  
101 101
    notification1 = Notification.notify(john_doe, 'notibar')
tests/test_public.py
138 138
    assert 'Foobar' not in resp.text
139 139

  
140 140

  
141
def test_page_contents_condition(app, normal_user):
142
    page = Page.objects.create(title='Home', slug='index', template_name='standard')
143
    TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=0, condition='True')
144
    TextCell.objects.create(page=page, placeholder='content', text='Foobaz', order=1, condition='False')
145
    cell3 = LinkListCell.objects.create(order=1, page=page, placeholder='content')
146
    LinkCell.objects.create(
147
        page=page,
148
        placeholder=cell3.link_placeholder,
149
        title='Example Site',
150
        url='http://example.net/',
151
        order=0,
152
        condition='True',
153
    )
154
    LinkCell.objects.create(
155
        page=page,
156
        placeholder=cell3.link_placeholder,
157
        title='Other Site',
158
        url='http://other.net/',
159
        order=1,
160
        condition='False',
161
    )
162

  
163
    app = login(app, username='normal-user', password='normal-user')
164
    resp = app.get('/', status=200)
165
    assert 'Foobar' in resp.text
166
    assert 'Foobaz' not in resp.text
167
    assert 'Example Site' in resp.text
168
    assert 'Other Site' not in resp.text
169

  
170

  
141 171
def test_page_footer_acquisition(app):
142 172
    Page.objects.all().delete()
143 173
    page = Page(title='Home', slug='index', template_name='standard')
tests/test_search.py
313 313

  
314 314

  
315 315
def test_search_cell_visibility(settings, app):
316
    request = RequestFactory().get('/')
316 317
    page = Page.objects.create(title='example page', slug='example-page')
317 318
    settings.COMBO_SEARCH_SERVICES = SEARCH_SERVICES
318 319

  
319 320
    cell = SearchCell(page=page, order=0)
320
    assert not cell.is_visible()
321
    assert not cell.is_visible(request)
321 322

  
322 323
    cell._search_services = {'data': ['_text']}
323
    assert cell.is_visible()
324
    assert cell.is_visible(request)
324 325

  
325 326

  
326 327
def test_search_contents():
......
1394 1395

  
1395 1396

  
1396 1397
def test_index_site_invalid_cell(app):
1398
    request = RequestFactory().get('/')
1397 1399
    page = Page.objects.create(title='page', slug='example-page')
1398 1400
    cell = TextCell.objects.create(page=page, placeholder='content', text='<p>foobar</p>', order=0)
1399 1401

  
......
1402 1404

  
1403 1405
    # invalid cell since just now
1404 1406
    cell.mark_as_invalid(reason_code='foobar')
1405
    assert cell.is_visible() is True
1407
    assert cell.is_visible(request) is True
1406 1408
    index_site()
1407 1409
    assert IndexedCell.objects.count() == 1
1408 1410

  
......
1410 1412
    validity_info = cell.get_validity_info()
1411 1413
    validity_info.invalid_since = now() - datetime.timedelta(days=2)
1412 1414
    validity_info.save()
1413
    assert cell.is_visible() is False
1415
    assert cell.is_visible(request) is False
1414 1416
    index_site()
1415 1417
    assert IndexedCell.objects.count() == 0
1416 1418

  
tests/test_wcs.py
35 35
    WcsFormsOfCategoryCell,
36 36
)
37 37
from combo.data.library import get_cell_classes
38
from combo.data.models import CellBase, LinkListCell, Page, ValidityInfo
38
from combo.data.models import CellBase, LinkCell, LinkListCell, Page, ValidityInfo
39 39
from combo.utils import NothingInCacheException
40 40

  
41 41
from .test_manager import login
......
1869 1869
    cell.only_for_user = False
1870 1870
    cell.save()
1871 1871

  
1872
    assert cell.is_visible(user=None) is True
1873
    assert cell.is_visible(user=MockUserWithNameId()) is True
1872
    assert cell.is_visible(request=context['request']) is True
1873
    context['request'].user = MockUserWithNameId()
1874
    assert cell.is_visible(request=context['request']) is True
1874 1875

  
1875 1876
    cell.only_for_user = True
1876 1877
    cell.save()
1877
    assert cell.is_visible(user=None) is False
1878
    assert cell.is_visible(user=MockUserWithNameId()) is True
1878
    context['request'].user = None
1879
    assert cell.is_visible(request=context['request']) is False
1880
    context['request'].user = MockUserWithNameId()
1881
    assert cell.is_visible(request=context['request']) is True
1879 1882

  
1880 1883
    cache.clear()
1881 1884
    context['synchronous'] = True  # to get fresh content
1885
    context['request'].user = None
1882 1886

  
1883 1887
    mock_send.reset_mock()
1884 1888
    cell.render(context)
......
4207 4211
    assert new_item.cached_json == item.cached_json
4208 4212

  
4209 4213

  
4214
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
4215
def test_cell_condition(mock_send, nocache, app):
4216
    page = Page.objects.create(title='xxx', slug='foo', template_name='standard')
4217
    cell = WcsCardInfosCell.objects.create(
4218
        page=page,
4219
        placeholder='content',
4220
        order=0,
4221
        carddef_reference='default:card_model_1',
4222
        card_ids='{{ cards|objects:"card_model_1"|last|get:"id" }}',
4223
    )
4224

  
4225
    cell.condition = 'cards|objects:"card_model_1"|getlist:"id"|list'
4226
    cell.save()
4227
    resp = app.get(page.get_online_url())
4228
    assert len(resp.context['cells']) == 1
4229

  
4230
    cell.condition = 'cards|objects:"card_model_1"|getlist:"id"|get:42'
4231
    cell.save()
4232
    resp = app.get(page.get_online_url())
4233
    assert len(resp.context.get('cells') or []) == 0
4234

  
4235
    page.extra_variables = {'var1': 'cards|objects:"card_model_1"|getlist:"id"|list'}
4236
    page.save()
4237
    cell.condition = 'var1'
4238
    cell.save()
4239
    resp = app.get(page.get_online_url())
4240
    assert len(resp.context['cells']) == 1
4241

  
4242
    page.extra_variables = {'var1': 'cards|objects:"card_model_1"|getlist:"id"|get:42'}
4243
    page.save()
4244
    resp = app.get(page.get_online_url())
4245
    assert len(resp.context.get('cells') or []) == 0
4246

  
4247

  
4248
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
4249
def test_link_list_cell_condition(mock_send, nocache, app):
4250
    page = Page.objects.create(title='xxx', slug='foo', template_name='standard')
4251
    cell = LinkListCell.objects.create(order=0, placeholder='content', page=page)
4252
    link_cell = LinkCell.objects.create(
4253
        page=page,
4254
        placeholder=cell.link_placeholder,
4255
        title='Example Site',
4256
        url='http://example.net/',
4257
        order=0,
4258
    )
4259

  
4260
    link_cell.condition = 'cards|objects:"card_model_1"|getlist:"id"|list'
4261
    link_cell.save()
4262
    resp = app.get(page.get_online_url())
4263
    assert len(resp.context['cells']) == 1
4264
    assert 'Example Site' in resp
4265

  
4266
    link_cell.condition = 'cards|objects:"card_model_1"|getlist:"id"|get:42'
4267
    link_cell.save()
4268
    resp = app.get(page.get_online_url())
4269
    assert len(resp.context['cells']) == 1
4270
    assert 'Example Site' not in resp
4271

  
4272
    page.extra_variables = {'var1': 'cards|objects:"card_model_1"|getlist:"id"'}
4273
    page.save()
4274
    link_cell.condition = 'var1'
4275
    link_cell.save()
4276
    resp = app.get(page.get_online_url())
4277
    assert len(resp.context['cells']) == 1
4278
    assert 'Example Site' in resp
4279

  
4280
    page.extra_variables = {'var1': 'cards|objects:"card_model_1"|getlist:"id"|get:42'}
4281
    page.save()
4282
    resp = app.get(page.get_online_url())
4283
    assert len(resp.context['cells']) == 1
4284
    assert 'Example Site' not in resp
4285

  
4286

  
4210 4287
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
4211 4288
def test_manager_add_edit_delete_list_link_item(mock_send, app, admin_user):
4212 4289
    page = Page.objects.create(title='One', slug='one', template_name='standard')
4213
-