diff --git a/g3w-admin/editing/forms.py b/g3w-admin/editing/forms.py index 44cba7e92..4e7fe4a37 100644 --- a/g3w-admin/editing/forms.py +++ b/g3w-admin/editing/forms.py @@ -83,6 +83,20 @@ class ActiveEditingLayerForm(ActiveEditingMixin, G3WRequestFormMixin, G3WProject #' more than 200 characters long.
' 'Value stored into the field it will be so structured: ' '[username]')) + add_user_group_field = forms.ChoiceField(choices=[], label=_('User Groups adding data field'), required=False, + help_text=_('Optional: select layer field to store ' + 'user groups name that entered the data. ' + 'Showed only string field.
' + # 'more than 200 characters long.
' + 'Value stored into the field it will be so structured: ' + '[user group name 1, user group name 2, ...]')) + edit_user_group_field = forms.ChoiceField(choices=[], label=_('User Groups editing data field'), required=False, + help_text=_('Optional: select layer field to store ' + 'user groups name that updated the data. ' + 'Showed only string field.
' + # ' more than 200 characters long.
' + 'Value stored into the field it will be so structured: ' + '[user group name 1, user group name 2, ...]')) def __init__(self, *args, **kwargs): @@ -117,6 +131,8 @@ def __init__(self, *args, **kwargs): layout_args += [ Field('add_user_field', css_class='select2', style="width:100%;"), Field('edit_user_field', css_class='select2', style="width:100%;"), + Field('add_user_group_field', css_class='select2', style="width:100%;"), + Field('edit_user_group_field', css_class='select2', style="width:100%;"), HTML(_('Select viewers with \'view permission\' on project that can edit layer:')), Field('viewer_users', css_class='select2', style="width:100%;"), Div(css_class='users_atomic_capabilities'), @@ -126,19 +142,56 @@ def __init__(self, *args, **kwargs): self.helper.layout = Layout(*layout_args) + def clean_add_user_field(self): + + # Create general error message + self.logging_fields_except = ValidationError(_("'User adding data field' and 'User editing data field' and " + "'User group adding data field' and 'User group editing data field' " + "must be unique.")) + + if self.cleaned_data['add_user_field'] and self.cleaned_data['add_user_field'] in ( + self.data.get('edit_user_field', None), + self.data.get('add_user_group_field', None), + self.data.get('edit_user_group_field', None)): + raise self.logging_fields_except + + return self.cleaned_data['add_user_field'] + def clean_edit_user_field(self): - if self.cleaned_data['edit_user_field'] and self.cleaned_data['add_user_field']: - if self.cleaned_data['edit_user_field'] == self.cleaned_data['add_user_field']: - raise ValidationError(_("'User adding data field' and 'User editing data field' " - "cannot assume the same value.")) + if self.cleaned_data['edit_user_field'] and self.cleaned_data['edit_user_field'] in ( + self.data.get('add_user_field', None), + self.data.get('add_user_group_field', None), + self.data.get('edit_user_group_field', None)): + raise self.logging_fields_except return self.cleaned_data['edit_user_field'] + def clean_add_user_group_field(self): + + if self.cleaned_data['add_user_group_field'] and self.cleaned_data['add_user_group_field'] in ( + self.data.get('edit_user_field', None), + self.data.get('add_user_field', None), + self.data.get('edit_user_group_field', None)): + raise self.logging_fields_except + + return self.cleaned_data['add_user_group_field'] + + def clean_edit_user_group_field(self): + + if self.cleaned_data['edit_user_group_field'] and self.cleaned_data['edit_user_group_field'] in ( + self.data.get('edit_user_field', None), + self.data.get('add_user_field', None), + self.data.get('add_user_group_field', None)): + raise self.logging_fields_except + + return self.cleaned_data['edit_user_group_field'] + def _set_add_edit_user_field_choices(self): """ - Set choices for add_user_field select and edit_user_field select + Set choices for add_user_field select and edit_user_field select and for + add_user_group_field, edit_user_group_field. """ touse = [] @@ -152,6 +205,9 @@ def _set_add_edit_user_field_choices(self): self.fields['edit_user_field'].choices = \ self.fields['add_user_field'].choices = [(None, '--------')] + [(f, f) for f in touse] + self.fields['edit_user_group_field'].choices = \ + self.fields['add_user_group_field'].choices = [(None, '--------')] + [(f, f) for f in touse] + class ActiveEditingMultiLayerForm(ActiveEditingMixin, G3WRequestFormMixin, G3WProjectFormMixin, forms.Form): """ @@ -174,6 +230,7 @@ class ActiveEditingMultiLayerForm(ActiveEditingMixin, G3WRequestFormMixin, G3WPr label=_('User viewer groups') ) + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/g3w-admin/editing/migrations/0014_auto_20241022_0657.py b/g3w-admin/editing/migrations/0014_auto_20241022_0657.py new file mode 100644 index 000000000..cc72e25e3 --- /dev/null +++ b/g3w-admin/editing/migrations/0014_auto_20241022_0657.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.25 on 2024-10-22 06:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('editing', '0013_g3weditinglayer_visible'), + ] + + operations = [ + migrations.AddField( + model_name='g3weditinglayer', + name='add_user_group_field', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='g3weditinglayer', + name='edit_user_group_field', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/g3w-admin/editing/migrations/0015_auto_20241022_0707.py b/g3w-admin/editing/migrations/0015_auto_20241022_0707.py new file mode 100644 index 000000000..c480bbdbe --- /dev/null +++ b/g3w-admin/editing/migrations/0015_auto_20241022_0707.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.25 on 2024-10-22 07:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('editing', '0014_auto_20241022_0657'), + ] + + operations = [ + migrations.AlterField( + model_name='g3weditinglayer', + name='add_user_group_field', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AlterField( + model_name='g3weditinglayer', + name='edit_user_group_field', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/g3w-admin/editing/models/editing.py b/g3w-admin/editing/models/editing.py index b6eb4f189..adabfb3ce 100644 --- a/g3w-admin/editing/models/editing.py +++ b/g3w-admin/editing/models/editing.py @@ -43,6 +43,8 @@ class G3WEditingLayer(models.Model): add_user_field = models.CharField(null=True, blank=True, max_length=255) edit_user_field = models.CharField(null=True, blank=True, max_length=255) visible = models.BooleanField(default=True, null=True, blank=True, help_text="Show layer in layers list to edit") + add_user_group_field = models.CharField(null=True, blank=True, max_length=255) + edit_user_group_field = models.CharField(null=True, blank=True, max_length=255) class Meta: app_label = 'editing' diff --git a/g3w-admin/editing/receivers.py b/g3w-admin/editing/receivers.py index 5e525f7ba..f61f7700f 100644 --- a/g3w-admin/editing/receivers.py +++ b/g3w-admin/editing/receivers.py @@ -26,6 +26,7 @@ ) from qdjango.vector import LayerVectorView, MODE_CONFIG from qdjango.models import GeoConstraintRule +from usersmanage.utils import get_user_groups from .models import ( G3WEditingFeatureLock, G3WEditingLayer, @@ -315,24 +316,52 @@ def fill_logging_fields(sender, **kwargs): mode = kwargs["mode"] user = kwargs["user"] + user_groups = ", ".join([g.name for g in get_user_groups(user)]) try: el = G3WEditingLayer.objects.get( app_name="qdjango", layer_id=kwargs["layer_metadata"].layer_id ) - if el.add_user_field and mode == EDITING_POST_DATA_ADDED: - kwargs["data"]["feature"]["properties"][ - el.add_user_field - ] = f"{user.username}" + if mode == EDITING_POST_DATA_ADDED: - # Remove edit_suer_field property if is active + # Set the user and user group + # --------------------------- + if el.add_user_field: + kwargs["data"]["feature"]["properties"][ + el.add_user_field + ] = f"{user.username}" + + # Remove edit_user_field property if is active + if el.edit_user_field: + del kwargs["data"]["feature"]["properties"][el.edit_user_field] + + if el.add_user_group_field: + kwargs["data"]["feature"]["properties"][ + el.add_user_group_field + ] = f"{user_groups}" + + # Remove edit_user_field property if is active + if el.edit_user_group_field: + del kwargs["data"]["feature"]["properties"][el.edit_user_group_field] + + + if mode == EDITING_POST_DATA_UPDATED: + + # Set the user and user group + # --------------------------- if el.edit_user_field: - del kwargs["data"]["feature"]["properties"][el.edit_user_field] - if el.edit_user_field and mode == EDITING_POST_DATA_UPDATED: - kwargs['data']['feature']['properties'][el.edit_user_field] = f"{user.username}" + kwargs['data']['feature']['properties'][el.edit_user_field] = f"{user.username}" + + # Remove add_user_field property if is active + if el.add_user_field: + del(kwargs['data']['feature']['properties'][el.add_user_field]) + + if el.edit_user_group_field: + kwargs['data']['feature']['properties'][el.edit_user_group_field] = f"{user_groups}" + + # Remove add_user_field property if is active + if el.add_user_group_field: + del (kwargs['data']['feature']['properties'][el.add_user_group_field]) - # Remove add_user_field property if is active - if el.add_user_field: - del(kwargs['data']['feature']['properties'][el.add_user_field]) except Exception as e: logger.error(f"[EDITING] - FILL LOGGING FIELDS: {e}") diff --git a/g3w-admin/editing/tests/data/logging_test.db b/g3w-admin/editing/tests/data/logging_test.db index 08ea2f2dd..3af39dc89 100644 Binary files a/g3w-admin/editing/tests/data/logging_test.db and b/g3w-admin/editing/tests/data/logging_test.db differ diff --git a/g3w-admin/editing/tests/test_api.py b/g3w-admin/editing/tests/test_api.py index 19ac43d17..466e586b4 100644 --- a/g3w-admin/editing/tests/test_api.py +++ b/g3w-admin/editing/tests/test_api.py @@ -997,7 +997,7 @@ def test_editing_fields_loggin_commit_mode_api(self): editing_layer = self.logging_project.instance.layer_set.all()[0] # Activate logging field - G3WEditingLayer.objects.create(app_name='qdjango', layer_id=editing_layer.pk, + el = G3WEditingLayer.objects.create(app_name='qdjango', layer_id=editing_layer.pk, add_user_field='insert_log', edit_user_field='update_log') # ADD @@ -1091,6 +1091,114 @@ def test_editing_fields_loggin_commit_mode_api(self): self.assertTrue(f'admin01' in qgs_feature.attribute('insert_log')) self.assertTrue(f'admin01' in qgs_feature.attribute('update_log')) + self.client.logout() + + # TEST AS TEST_USER4 (VIEWER) + # ----------------------------------------------------------------- + assign_perm('view_project', self.test_user4, self.logging_project.instance) + self.client.login(username=self.test_user4.username, password=self.test_user4.username) + + el.add_user_field = '' + el.add_user_group_field = 'insert_log' + el.edit_user_field = '' + el.edit_user_group_field = 'update_log' + + el.save() + + # Set permission for user + assign_perm('add_feature', self.test_user4, editing_layer) + assign_perm('change_feature', self.test_user4, editing_layer) + assign_perm('delete_feature', self.test_user4, editing_layer) + assign_perm('change_attr_feature', self.test_user4, editing_layer) + assign_perm('change_layer', self.test_user4, editing_layer) + + # INSERT + # ====== + payload = { + "add": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -18219.126089871206, + -9298.036763106677 + ] + }, + "properties": { + "name": "test data 3", + "insert_log": None, + "update_log": None + }, + "id": "_new_76_1648018518851" + } + ], + "update": [], + "delete": [], + "relations": {}, + "lockids": [] + } + + response = self.client.post(commit_path, payload, format='json') + self.assertEqual(response.status_code, 200) + + jresult = json.loads(response.content) + self.assertTrue(jresult['result']) + + newid = jresult['response']['new'][0]['id'] + newlockid = jresult['response']['new_lockids'][0]['lockid'] + + qgs_feature = editing_layer.qgis_layer.getFeature(int(newid)) + + self.assertTrue(f'Viewer user group1' in qgs_feature.attribute('insert_log')) + self.assertFalse(qgs_feature.attribute('update_log')) + + # UPDATE + # ====== + + payload = { + "update": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -18219.126089871206, + -9298.036763106677 + ] + }, + "properties": { + "name": "test data 3", + "insert_log": None, + "update_log": None + }, + "id": newid + } + ], + "add": [], + "delete": [], + "relations": {}, + "lockids": [ + { + "featureid": newid, + "lockid": newlockid + } + ], + } + + response = self.client.post(commit_path, payload, format='json') + self.assertEqual(response.status_code, 200) + + jresult = json.loads(response.content) + self.assertTrue(jresult['result']) + + qgs_feature = editing_layer.qgis_layer.getFeature(int(newid)) + + self.assertTrue(f'Viewer user group1' in qgs_feature.attribute('insert_log')) + self.assertTrue(f'Viewer user group1' in qgs_feature.attribute('update_log')) + + self.client.logout() + def test_editing_provider_default_value(self): """ Test Editing API mode: MODE_COMMIT with fields having provider default values """ diff --git a/g3w-admin/editing/tests/test_views.py b/g3w-admin/editing/tests/test_views.py index 6c21cb1e8..a4b41153a 100644 --- a/g3w-admin/editing/tests/test_views.py +++ b/g3w-admin/editing/tests/test_views.py @@ -593,7 +593,9 @@ def test_editing_layer_active_logging_fields(self): data = { 'active': 'on', 'add_user_field': 'insert_log', - 'edit_user_field': 'insert_log' + 'edit_user_field': 'insert_log', + 'add_user_group_field': 'insert_log', + 'edit_user_group_field': 'insert_log' } res = self.client.post(url, data) @@ -605,11 +607,15 @@ def test_editing_layer_active_logging_fields(self): self.assertTrue(len(editing_layers) == 1) self.assertFalse(editing_layers[0].add_user_field) self.assertFalse(editing_layers[0].edit_user_field) + self.assertFalse(editing_layers[0].add_user_group_field) + self.assertFalse(editing_layers[0].edit_user_group_field) data = { 'active': 'on', 'add_user_field': 'insert_log', - 'edit_user_field': 'update_log' + 'edit_user_field': 'update_log', + 'add_user_group_field': '', + 'edit_user_group_field': '' } res = self.client.post(url, data) @@ -621,9 +627,13 @@ def test_editing_layer_active_logging_fields(self): self.assertTrue(len(editing_layers) == 1) self.assertEqual(editing_layers[0].add_user_field, 'insert_log') self.assertEqual(editing_layers[0].edit_user_field, 'update_log') + self.assertFalse(editing_layers[0].add_user_group_field) + self.assertFalse(editing_layers[0].edit_user_group_field) data = { 'active': 'on', + 'add_user_group_field': 'insert_log', + 'edit_user_group_field': 'update_log' } res = self.client.post(url, data) @@ -635,6 +645,29 @@ def test_editing_layer_active_logging_fields(self): self.assertTrue(len(editing_layers) == 1) self.assertFalse(editing_layers[0].add_user_field) self.assertFalse(editing_layers[0].edit_user_field) + self.assertEqual(editing_layers[0].add_user_group_field, 'insert_log') + self.assertEqual(editing_layers[0].edit_user_group_field, 'update_log') + + + data = { + 'active': 'on', + 'add_user_field': '', + 'edit_user_field': '', + 'add_user_group_field': '', + 'edit_user_group_field': '' + } + + res = self.client.post(url, data) + + # redirect on ok results + self.assertEqual(res.status_code, 302) + + editing_layers = G3WEditingLayer.objects.filter(layer_id=editing_layer.pk) + self.assertTrue(len(editing_layers) == 1) + self.assertFalse(editing_layers[0].add_user_field) + self.assertFalse(editing_layers[0].edit_user_field) + self.assertFalse(editing_layers[0].add_user_group_field) + self.assertFalse(editing_layers[0].edit_user_group_field) diff --git a/g3w-admin/editing/views.py b/g3w-admin/editing/views.py index facfd2178..6d96fc606 100644 --- a/g3w-admin/editing/views.py +++ b/g3w-admin/editing/views.py @@ -180,6 +180,8 @@ def get_form_kwargs(self): kwargs['initial']['scale'] = self.activated.scale kwargs['initial']['add_user_field'] = self.activated.add_user_field kwargs['initial']['edit_user_field'] = self.activated.edit_user_field + kwargs['initial']['add_user_group_field'] = self.activated.add_user_group_field + kwargs['initial']['edit_user_group_field'] = self.activated.edit_user_group_field except: self.activated = None kwargs['initial']['active'] = False @@ -275,6 +277,8 @@ def form_valid(self, form): visible = form.cleaned_data['visible'] add_user_field = form.cleaned_data['add_user_field'] edit_user_field = form.cleaned_data['edit_user_field'] + add_user_group_field = form.cleaned_data['add_user_group_field'] + edit_user_group_field = form.cleaned_data['edit_user_group_field'] if form.cleaned_data['active']: if not self.activated: @@ -283,12 +287,17 @@ def form_valid(self, form): scale=scale, visible=visible, add_user_field=add_user_field, - edit_user_field=edit_user_field) + edit_user_field=edit_user_field, + add_user_group_field=add_user_group_field, + edit_user_group_field=edit_user_group_field + ) else: self.activated.visible = visible self.activated.scale = scale self.activated.add_user_field = add_user_field self.activated.edit_user_field = edit_user_field + self.activated.add_user_group_field = add_user_group_field + self.activated.edit_user_group_field = edit_user_group_field self.activated.save() else: if self.activated: @@ -431,6 +440,8 @@ def form_valid(self, form): add_user_field = self.request.POST.get(f'add_user_field_{layer_pk}', None) edit_user_field = self.request.POST.get(f'edit_user_field_{layer_pk}', None) + add_user_group_field = self.request.POST.get(f'add_user_group_field_{layer_pk}', None) + edit_user_group_field = self.request.POST.get(f'edit_user_group_field_{layer_pk}', None) if form.cleaned_data['active']: @@ -439,6 +450,8 @@ def form_valid(self, form): layer_id=layer_pk, edit_user_field=edit_user_field, add_user_field=add_user_field, + edit_user_group_field=edit_user_group_field, + add_user_group_field=add_user_group_field, scale=scale, visible=visible) else: @@ -446,6 +459,8 @@ def form_valid(self, form): activated[layer_pk].scale = scale activated[layer_pk].add_user_field = add_user_field activated[layer_pk].edit_user_field = edit_user_field + activated[layer_pk].add_user_group_field = add_user_group_field + activated[layer_pk].edit_user_group_field = edit_user_group_field activated[layer_pk].save() else: if layer_pk in activated.keys(): diff --git a/g3w-admin/qdjango/templates/qdjango/ajax/project_detail.html b/g3w-admin/qdjango/templates/qdjango/ajax/project_detail.html index d277d4a19..c95e6597d 100644 --- a/g3w-admin/qdjango/templates/qdjango/ajax/project_detail.html +++ b/g3w-admin/qdjango/templates/qdjango/ajax/project_detail.html @@ -101,6 +101,8 @@

Layer: {{ e.elayer.layer.name }}

{% trans 'Scale' %}: {% if e.elayer.scale %}1:{{ e.elayer.scale }}{% endif %}
{% trans 'User adding data field'%}: {% if e.elayer.add_user_field %}{{ e.elayer.add_user_field }}{% endif %}
{% trans 'User editing data field '%}: {% if e.elayer.edit_user_field %}{{ e.elayer.edit_user_field }}{% endif %}
+
{% trans 'User group adding data field'%}: {% if e.elayer.add_user_group_field %}{{ e.elayer.add_user_group_field }}{% endif %}
+
{% trans 'User group editing data field '%}: {% if e.elayer.edit_user_group_field %}{{ e.elayer.edit_user_group_field }}{% endif %}
{% if e.users %}