diff --git a/CHANGELOG.md b/CHANGELOG.md index b4070e9c..4edf8b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## Unreleased + +## [1.1.1] - 2020-10-29 + +### Changed +- increase thickness of segments of features and reduced the transparency of dotted features + +### Fixed +- The creator is correctly displayed in the features and the feature types +- In the basemaps form, the display of a very long layer name is now responsive +- A browser title (tab) is now displayed for all pages +- Projects with limited access are no longer accessible to everyone +- The features are now filtered when search on the map +- The search in the list of features now stay in the same page +- The Georchestra plugin now keeps user rights defined in GeoContrib +- Draft features are now hidden on the section "Last features" +- Empty comments are now blocked + ## [1.1.0] - 2020-08-28 ### Changed @@ -15,7 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [1.1.0-rc1] - 2020-08-19 ### Added -- geOrchestra plugin: automatically associate role to users when the user database is synchronised (see +- geOrchestra plugin: automatically associate role to users when the user database is synchronised (see [geOrchestra plugin](plugin_georchestra/README.md)) - add a function to search for places and addresses in the interactive maps. This new feature comes with new settings: `GEOCODER_PROVIDERS` and `SELECTED_GEOCODER` @@ -29,10 +47,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - change the label of the feature type field `title` in the front-end form (Titre -> Nom) - change the data model for basemaps: one basemap may contain several layers. Layers are declared by GéoContrib admin users. Basemaps are created by project admin users by selecting layers, ordering them and setting the opacity -of each of them. End users may switch from one basemap to another in the interactive maps. One user can change -the order of the layers and their opacity in the interactive maps. These personnal adjustments are stored in the +of each of them. End users may switch from one basemap to another in the interactive maps. One user can change +the order of the layers and their opacity in the interactive maps. These personnal adjustments are stored in the web browser of the user (local storage) and do not alter the basemaps as seen by other users. -- change default value for `LOGO_PATH` setting: Neogeo Technologie logo. This new image is located in the media +- change default value for `LOGO_PATH` setting: Neogeo Technologie logo. This new image is located in the media directory. - change all visible names in front-end and docs from `Geocontrib` to `GéoContrib` - set the leaflet background container to white diff --git a/api/serializers.py b/api/serializers.py index aacb1ecd..35d7bc49 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -80,7 +80,7 @@ class UserSerializer(serializers.ModelSerializer): full_name = serializers.SerializerMethodField() def get_full_name(self, obj): - return obj.get_full_name() + return obj.get_full_name() or obj.username class Meta: model = User @@ -99,7 +99,7 @@ class CommentSerializer(serializers.ModelSerializer): created_on = serializers.DateTimeField(format="%d/%m/%Y", read_only=True) - author = UserSerializer(read_only=True) + display_author = serializers.ReadOnlyField() related_feature = serializers.SerializerMethodField() @@ -122,7 +122,7 @@ class Meta: fields = ( 'created_on', 'comment', - 'author', + 'display_author', 'related_feature', ) @@ -267,7 +267,7 @@ def get_feature_to(self, obj): 'title': str(feature.title), 'feature_url': feature.get_view_url(), 'created_on': feature.created_on.strftime("%d/%m/%Y %H:%M"), - 'creator': feature.creator.display_creator, + 'creator': feature.display_creator, } except Exception: logger.exception('No related feature found') @@ -285,7 +285,7 @@ class EventSerializer(serializers.ModelSerializer): created_on = serializers.DateTimeField(format="%d/%m/%Y %H:%M", read_only=True) - user = UserSerializer(read_only=True) + display_user = serializers.ReadOnlyField() related_comment = serializers.SerializerMethodField() @@ -344,7 +344,7 @@ class Meta: 'feature_id', 'comment_id', 'attachment_id', - 'user', + 'display_user', 'related_comment', 'related_feature', 'project_url', diff --git a/docs/users.md b/docs/users.md index 8862affc..2d29fe96 100644 --- a/docs/users.md +++ b/docs/users.md @@ -9,6 +9,7 @@ Autorisations attribuables par projet : Autorisations indépendantes des projets : * Super utilisateur * Gestionnaire métier +* Statut équipe ## Autorisations liées aux projets @@ -89,3 +90,9 @@ Un gestionnaire métier peut : Le créateur d'un nouveau projet en devient automatiquement administrateur projet du projet en question. + +### Statut équipe + +Un utilisateur avec le statut équipe peut se connecter à l'interface administrateur. +Il accède aux fonctionnalités de l'interface selon les permissions qui lui ont été accordées. + diff --git a/geocontrib/context_processors.py b/geocontrib/context_processors.py index f6f0ace7..42687b65 100644 --- a/geocontrib/context_processors.py +++ b/geocontrib/context_processors.py @@ -1,7 +1,8 @@ +import logging + from django.conf import settings + from geocontrib.models import Authorization -import json -import logging logger = logging.getLogger(__name__) diff --git a/geocontrib/emails.py b/geocontrib/emails.py index 194af091..ba12a36d 100644 --- a/geocontrib/emails.py +++ b/geocontrib/emails.py @@ -76,7 +76,7 @@ def notif_creator_published_feature(emails, context): context['url_feature'] = urljoin(CURRENT_SITE_DOMAIN, feature.get_view_url()) - subject = "[Collab:{project_slug}] Confirmation de la publication de l'un de vos signalement.".format( + subject = "[Collab:{project_slug}] Confirmation de la publication de l'un de vos signalements.".format( project_slug=feature.project.slug ) diff --git a/geocontrib/forms.py b/geocontrib/forms.py index 8ce0131e..e898446b 100644 --- a/geocontrib/forms.py +++ b/geocontrib/forms.py @@ -114,7 +114,7 @@ def clean(self): continue name = form.cleaned_data.get('name') if name in names: - raise forms.ValidationError("Les champs supplémentaires ne peuvent avoir des nom similaires.") + raise forms.ValidationError("Les champs supplémentaires ne peuvent avoir des noms similaires.") names.append(name) @@ -149,7 +149,7 @@ class CommentForm(forms.ModelForm): attachment_file = forms.FileField(label="Fichier joint", required=False) info = forms.CharField( - label="Information additonelle au fichier joint", required=False, widget=forms.Textarea()) + label="Information additionnelle au fichier joint", required=False, widget=forms.Textarea()) class Meta: model = Comment @@ -205,7 +205,7 @@ class AuthorizationForm(forms.ModelForm): username = forms.CharField(label="Nom d'utilisateur") - email = forms.EmailField(label="Adresse email") + email = forms.EmailField(label="Adresse email", required=False) level = forms.ModelChoiceField( label="Niveau d'autorisation", @@ -414,7 +414,7 @@ def __init__(self, *args, **kwargs): self.fields['feature_to'].choices = tuple( (feat.feature_id, "{} ({} - {})".format( - feat.title, feat.creator.display_creator, feat.created_on.strftime("%d/%m/%Y %H:%M"))) for feat in qs + feat.title, feat.display_creator, feat.created_on.strftime("%d/%m/%Y %H:%M"))) for feat in qs ) except Exception: diff --git a/geocontrib/models.py b/geocontrib/models.py index 27a9c651..1d1e2052 100644 --- a/geocontrib/models.py +++ b/geocontrib/models.py @@ -156,7 +156,7 @@ def all_permissions(cls, user, project, feature=None): user_rank = cls.get_rank(user, project) - if user_rank >= project_rank_min or project_rank_min < 2: + if user_rank >= project_rank_min or project_rank_min == 0: user_perms['can_view_project'] = True user_perms['can_view_feature'] = True user_perms['can_view_feature_type'] = True @@ -592,6 +592,13 @@ def save(self, *args, **kwargs): self.created_on = timezone.now() super().save(*args, **kwargs) + @property + def display_author(self): + res = "Utilisateur supprimé" + if self.author: + res = self.author.get_full_name() or self.author.username + return res + class Attachment(AnnotationAbstract): @@ -684,6 +691,13 @@ def save(self, *args, **kwargs): self.created_on = timezone.now() super().save(*args, **kwargs) + @property + def display_user(self): + res = "Utilisateur supprimé" + if self.user: + res = self.user.get_full_name() or self.user.username + return res + @property def contextualize_action(self): evt = 'Aucun evenement' diff --git a/geocontrib/static/geocontrib/css/base.css b/geocontrib/static/geocontrib/css/base.css index c1e351af..40924439 100644 --- a/geocontrib/static/geocontrib/css/base.css +++ b/geocontrib/static/geocontrib/css/base.css @@ -64,6 +64,15 @@ main { color: #9f3a38; } +/* Fix semantic ui overflow when is too long */ +.layer-item .form div.text { + width: 100% +} + +.ui.selection.dropdown .menu>.item { + word-break: break-all; +} + /* Needs this unfortunatly, because semantic overrides the background-color */ #form-layers .blue-background-class { background-color: rgb(205, 229, 245); diff --git a/geocontrib/static/geocontrib/js/map-util.js b/geocontrib/static/geocontrib/js/map-util.js index 44fa1289..c8307fdd 100644 --- a/geocontrib/static/geocontrib/js/map-util.js +++ b/geocontrib/static/geocontrib/js/map-util.js @@ -99,37 +99,50 @@ const mapUtil = { this.addLayers(layers); }, - addFeatures: function (features) { + addFeatures: function (features, filter) { featureGroup = new L.FeatureGroup(); features.forEach((feature) => { - const geomJSON = turf.flip(feature.geometry); - const popupContent = this._createContentPopup(feature); + let filters = []; - if (geomJSON.type === 'Point') { - L.circleMarker(geomJSON.coordinates, { - color: feature.properties.feature_type.color, - radius: 4, - fillOpacity: 0.3, - weight: 1, - }) - .bindPopup(popupContent) - .addTo(featureGroup); - } else if (geomJSON.type === 'LineString') { - L.polyline(geomJSON.coordinates, { - color: feature.properties.feature_type.color, - weight: 1.5, - }) - .bindPopup(popupContent) - .addTo(featureGroup); - } else if (geomJSON.type === 'Polygon') { - L.polygon(geomJSON.coordinates, { - color: feature.properties.feature_type.color, - weight: 1.5, - fillOpacity: 0.3, - }) - .bindPopup(popupContent) - .addTo(featureGroup); + if (filter) { + const typeCheck = filter.featureType && feature.properties.feature_type.slug === filter.featureType; + const statusCheck = filter.featureStatus && feature.properties.status.value === filter.featureStatus; + const titleCheck = filter.featureTitle && feature.properties.title.includes(filter.featureTitle); + filters = [typeCheck, statusCheck, titleCheck]; + } + + if (!filter || !Object.values(filter).some(val => val) || Object.values(filter).some(val => val) && filters.length && filters.every(val => val !== false)) { + + const geomJSON = turf.flip(feature.geometry); + + const popupContent = this._createContentPopup(feature); + + if (geomJSON.type === 'Point') { + L.circleMarker(geomJSON.coordinates, { + color: feature.properties.feature_type.color, + radius: 4, + fillOpacity: 0.5, + weight: 3, + }) + .bindPopup(popupContent) + .addTo(featureGroup); + } else if (geomJSON.type === 'LineString') { + L.polyline(geomJSON.coordinates, { + color: feature.properties.feature_type.color, + weight: 3, + }) + .bindPopup(popupContent) + .addTo(featureGroup); + } else if (geomJSON.type === 'Polygon') { + L.polygon(geomJSON.coordinates, { + color: feature.properties.feature_type.color, + weight: 3, + fillOpacity: 0.5, + }) + .bindPopup(popupContent) + .addTo(featureGroup); + } } }); map.addLayer(featureGroup); diff --git a/geocontrib/templates/geocontrib/base.html b/geocontrib/templates/geocontrib/base.html index d8b91972..68a4f3a5 100644 --- a/geocontrib/templates/geocontrib/base.html +++ b/geocontrib/templates/geocontrib/base.html @@ -42,6 +42,9 @@ + + + @@ -52,7 +55,7 @@ -
+
Création du signalement - {% if user.is_authenticated %} par {{ event.user.full_name }}{% endif %} + {% if user.is_authenticated %} par {{ event.display_user }}{% endif %}
@@ -180,7 +180,7 @@

Activité et commentaires

{{ event.created_on }} Commentaire - {% if user.is_authenticated %} par {{ event.user.full_name }}{% endif %} + {% if user.is_authenticated %} par {{ event.display_user }}{% endif %}
{{ event.related_comment.comment }} @@ -201,7 +201,7 @@

Activité et commentaires

{{ event.created_on }}
Signalement mis à jour - {% if user.is_authenticated %} par {{ event.user.full_name }}{% endif %} + {% if user.is_authenticated %} par {{ event.display_user }}{% endif %} @@ -224,8 +224,8 @@

Activité et commentaires

{% endif %}
- {{ comment_form.comment.errors }} +
@@ -239,8 +239,7 @@

Activité et commentaires

{{ comment_form.attachment_file.errors }}
- + {{ comment_form.title.errors }}
diff --git a/geocontrib/templates/geocontrib/feature/feature_list.html b/geocontrib/templates/geocontrib/feature/feature_list.html index abdcdae5..c00119e3 100644 --- a/geocontrib/templates/geocontrib/feature/feature_list.html +++ b/geocontrib/templates/geocontrib/feature/feature_list.html @@ -3,6 +3,8 @@ {% load static %} {% load app_filters %} +{% block title %}{{ title }}{% endblock %} + {% block content %}
@@ -144,11 +146,7 @@

{{ features|length }} signalement{% if features|length > 1 %}s{% endif %} {% if user.is_authenticated %} - {% if feature.creator %} - {{ feature.creator.first_name }} {{ feature.creator.last_name }} - {% else %} - Utilisateur supprimé - {% endif %} + {{ feature.display_creator }} {% endif %} @@ -197,7 +195,7 @@

{{ features|length }} signalement{% if features|length > 1 %}s{% endif %} @@ -216,21 +214,26 @@

{{ features|length }} signalement{% if features|length > 1 %}s{% endif %} function getDataFilters() { var $form = $("#form-filters").serializeArray() - var requestURL = `{% url 'geocontrib:feature_list' slug=project.slug %}` + var requestURL = ''; for (var field of $form) { if (field.value) { if (requestURL.includes('?')) { requestURL = `${requestURL}&${field.name}=${field.value}` } else { - requestURL = `${requestURL}?${field.name}=${field.value}` + requestURL = `?${field.name}=${field.value}` } } } - document.location = requestURL + document.location.search = requestURL } $(document).ready(function () { + $('.ui.menu .item').tab({ + history: true, + historyType: 'hash' + }); + $('#form-filters .ui.selection.dropdown').dropdown({ clearable: true }) @@ -321,7 +324,11 @@

{{ features|length }} signalement{% if features|length > 1 %}s{% endif %} 0) { mapUtil.getMap().fitBounds(featureGroup.getBounds()) diff --git a/geocontrib/templates/geocontrib/feature_type/feature_type_detail.html b/geocontrib/templates/geocontrib/feature_type/feature_type_detail.html index 5cfdc858..5e1b55c9 100644 --- a/geocontrib/templates/geocontrib/feature_type/feature_type_detail.html +++ b/geocontrib/templates/geocontrib/feature_type/feature_type_detail.html @@ -123,7 +123,7 @@

[ Créé le {{ feature.created_on }} {% if user.is_authenticated %} - par {{ feature.creator.display_creator }} + par {{ feature.display_creator }} {% endif %} ]

diff --git a/geocontrib/templates/geocontrib/my_account.html b/geocontrib/templates/geocontrib/my_account.html index a646a321..a5c136b7 100644 --- a/geocontrib/templates/geocontrib/my_account.html +++ b/geocontrib/templates/geocontrib/my_account.html @@ -139,7 +139,7 @@

MES PROJETS

{% endif %}
- [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.user.full_name }}{% endif %} ] + [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.display_user }}{% endif %} ]
@@ -166,7 +166,7 @@

MES PROJETS

{% endif %}
- [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.user.full_name }}{% endif %} ] + [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.display_user }}{% endif %} ]
@@ -189,7 +189,7 @@

MES PROJETS

"{{ item.related_comment.comment }}"
- [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.user.full_name }}{% endif %} ] + [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.display_user }}{% endif %} ]
diff --git a/geocontrib/templates/geocontrib/project/project_home.html b/geocontrib/templates/geocontrib/project/project_home.html index 1c56ff6c..b0de02c0 100644 --- a/geocontrib/templates/geocontrib/project/project_home.html +++ b/geocontrib/templates/geocontrib/project/project_home.html @@ -2,7 +2,7 @@ {% load static %} -{% block title %}Projet {{ project.title }}{% endblock %} +{% block title %}{{ title }}{% endblock %} {% block content %} @@ -115,7 +115,7 @@

Types de signalements

{{ item.title }}
- [ {{ item.created_on|date:"d/m/Y" }}{% if user.is_authenticated %}, par {{ item.creator.first_name }} {{ item.creator.last_name }}{% endif %} ] + [ {{ item.created_on|date:"d/m/Y" }}{% if user.is_authenticated %}, par {{ item.display_creator }} {% endif %} ]
@@ -138,7 +138,7 @@

Types de signalements

"{{ item.comment }}"
- [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.author.full_name }}{% endif %} ] + [ {{ item.created_on }}{% if user.is_authenticated %}, par {{ item.display_author }}{% endif %} ]
@@ -273,7 +273,7 @@

// Load into js variables the python context. const baseMaps = JSON.parse(document.getElementById('basemaps').textContent); const layers = JSON.parse(document.getElementById('layers').textContent); - const features = JSON.parse(document.getElementById('features').textContent) ? + const features = JSON.parse(document.getElementById('features').textContent) ? JSON.parse(document.getElementById('features').textContent).features : null; const serviceMap = JSON.parse(document.getElementById('serviceMap').textContent); const optionsMap = JSON.parse(document.getElementById('optionsMap').textContent); diff --git a/geocontrib/templates/geocontrib/registration/login.html b/geocontrib/templates/geocontrib/registration/login.html index 638d4016..c30ead5b 100644 --- a/geocontrib/templates/geocontrib/registration/login.html +++ b/geocontrib/templates/geocontrib/registration/login.html @@ -22,9 +22,9 @@

CONNEXION

{% if form.errors %}
- Les informations d'identifications sont incorrectes. + Les informations d'identification sont incorrectes.
- NB: Seules les comptes actifs peuvent se connecter. + NB: Seuls les comptes actifs peuvent se connecter.
{% endif %}
diff --git a/geocontrib/views/accounts.py b/geocontrib/views/accounts.py index 33e9d6a3..5afd1d9c 100644 --- a/geocontrib/views/accounts.py +++ b/geocontrib/views/accounts.py @@ -101,5 +101,6 @@ def get(self, request): context['events'] = serialized_events.data context['features'] = serialized_feature_events.data context['comments'] = serialized_comment_events.data + context['title'] = "Mon compte" return render(request, 'geocontrib/my_account.html', context) diff --git a/geocontrib/views/content_managment.py b/geocontrib/views/content_managment.py index f32f5ddc..5b908982 100644 --- a/geocontrib/views/content_managment.py +++ b/geocontrib/views/content_managment.py @@ -80,6 +80,9 @@ def get_context_data(self, **kwargs): try: request = self.request project = None + title = None + if any([isinstance(self.object, model) for model in [Project, FeatureType, Feature]]): + title = self.object.title if isinstance(self.object, Project): project = self.object elif isinstance(self.object, FeatureType) or isinstance(self.object, Feature): @@ -102,6 +105,7 @@ def get_context_data(self, **kwargs): context['serialized_features'] = serialized_features.data context['serialized_base_maps'] = serialized_base_maps.data context['serialized_layers'] = serialized_layers.data + context['title'] = title except Exception: logger.exception('BaseMapContext error') return context @@ -157,7 +161,7 @@ def post(self, request, slug, feature_type_slug, feature_id): @method_decorator(DECORATORS, name='dispatch') -class CommentCreate(SingleObjectMixin, UserPassesTestMixin, View): +class CommentCreate(BaseMapContextMixin, UserPassesTestMixin, View): queryset = Feature.objects.all() pk_url_kwarg = 'feature_id' @@ -167,6 +171,13 @@ def test_func(self): project = feature.project return Authorization.has_permission(user, 'can_create_feature', project) + def get(self, request, slug, feature_type_slug, feature_id): + return redirect( + 'geocontrib:feature_detail', + slug=slug, + feature_type_slug=feature_type_slug, + feature_id=feature_id) + def post(self, request, slug, feature_type_slug, feature_id): feature = self.get_object() project = feature.project @@ -223,7 +234,7 @@ def post(self, request, slug, feature_type_slug, feature_id): events = Event.objects.filter(feature_id=feature.feature_id).order_by('created_on') serialized_events = EventSerializer(events, many=True) - context = { + context = {**self.get_context_data(), **{ 'feature': feature, 'feature_data': feature.custom_fields_as_list, 'feature_types': FeatureType.objects.filter(project=project), @@ -235,8 +246,8 @@ def post(self, request, slug, feature_type_slug, feature_id): 'attachments': Attachment.objects.filter( project=project, feature_id=feature.feature_id, object_type='feature'), 'events': serialized_events.data, - 'comment_form': CommentForm(), - } + 'comment_form': form, + }} return render(request, 'geocontrib/feature/feature_detail.html', context) @@ -423,7 +434,7 @@ def post(self, request, slug, feature_type_slug): attachment_formset = self.AttachmentFormset( request.POST or None, request.FILES, prefix='attachment') - context = { + context = {**self.get_context_data(), **{ 'features': Feature.handy.availables(user, project).order_by('updated_on'), 'feature_type': feature_type, 'project': project, @@ -433,7 +444,7 @@ def post(self, request, slug, feature_type_slug): 'linked_formset': linked_formset, 'attachment_formset': attachment_formset, 'action': 'create', - } + }} return render(request, 'geocontrib/feature/feature_edit.html', context) @@ -808,6 +819,7 @@ def get(self, request, slug): 'permissions': Authorization.all_permissions(user, project), 'feature_types': project.featuretype_set.all(), 'project': project, + 'title': "Création d'un type de signalement", } return render(request, 'geocontrib/feature_type/feature_type_create.html', context) @@ -840,6 +852,7 @@ def post(self, request, slug): 'permissions': Authorization.all_permissions(user, project), 'feature_types': project.featuretype_set.all(), 'project': project, + 'title': "Création d'un type de signalement", } return render(request, 'geocontrib/feature_type/feature_type_create.html', context) @@ -873,7 +886,8 @@ def get(self, request, slug, feature_type_slug): 'feature_types': project.featuretype_set.all(), 'features': features, 'project': project, - 'structure': structure.data + 'structure': structure.data, + 'title': feature_type.title, } return render(request, 'geocontrib/feature_type/feature_type_detail.html', context) @@ -925,6 +939,7 @@ def get(self, request, slug, feature_type_slug): 'structure': structure.data, 'form': form, 'formset': formset, + 'title': feature_type.title, } return render(request, 'geocontrib/feature_type/feature_type_edit.html', context) @@ -972,6 +987,7 @@ def post(self, request, slug, feature_type_slug): 'permissions': Authorization.all_permissions(user, feature_type.project), 'project': feature_type.project, 'feature_type': feature_type, + 'title': feature_type.title } return render(request, 'geocontrib/feature_type/feature_type_edit.html', context) @@ -1172,21 +1188,26 @@ def get_context_data(self, **kwargs): project = self.get_object() permissions = Authorization.all_permissions(user, project) - last_comments = Comment.objects.filter( + # On filtre les signalements selon leur statut et l'utilisateur courant + features = Feature.handy.availables( + user=user, project=project + ).order_by('-created_on') + + # On filtre les commentaire selon les signalements visibles + last_comments = Comment.objects.filter( + project=project, + feature_id__in=[feat.feature_id for feat in features] ).order_by('-created_on')[0:5] serialized_comments = CommentSerializer(last_comments, many=True).data - features = Feature.objects.filter( - project=project - ).order_by('-created_on') - serilized_projects = ProjectDetailedSerializer(project).data context = super().get_context_data(**kwargs) context['project'] = serilized_projects + context['title'] = project.title context['user'] = user context['last_comments'] = serialized_comments context['last_features'] = features[0:5] @@ -1217,7 +1238,8 @@ def get(self, request, slug): 'permissions': Authorization.all_permissions(request.user, project), 'feature_types': project.featuretype_set.all(), 'is_suscriber': Subscription.is_suscriber(request.user, project), - 'action': 'update' + 'action': 'update', + 'title': project.title, } return render(request, 'geocontrib/project/project_edit.html', context) @@ -1234,7 +1256,8 @@ def post(self, request, slug): 'permissions': Authorization.all_permissions(request.user, project), 'feature_types': project.featuretype_set.all(), 'is_suscriber': Subscription.is_suscriber(request.user, project), - 'action': 'update' + 'action': 'update', + 'title': project.title, } return render(request, 'geocontrib/project/project_edit.html', context) @@ -1259,6 +1282,7 @@ def form_valid(self, form): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['action'] = 'create' + context['title'] = "Création d'un projet" return context @@ -1283,6 +1307,7 @@ def get(self, request, slug): 'project': project, 'permissions': permissions, 'formset': formset, + 'title': project.title, }} return render(request, 'geocontrib/project/project_mapping.html', context) @@ -1304,7 +1329,8 @@ def post(self, request, slug): context = {**self.get_context_data(), **{ 'project': project, 'permissions': permissions, - 'formset': formset + 'formset': formset, + 'title': project.title, }} return render(request, 'geocontrib/project/project_mapping.html', context) @@ -1353,6 +1379,7 @@ def post(self, request, slug): formset = self.AuthorizationFormSet(request.POST or None) authorised = Authorization.objects.filter(project=project) permissions = Authorization.all_permissions(user, project) + if formset.is_valid(): for data in formset.cleaned_data: @@ -1374,16 +1401,17 @@ def post(self, request, slug): authorization.save() return redirect('geocontrib:project_members', slug=slug) - - context = { - "title": "Gestion des membres du projet {}".format(project.title), - 'authorised': authorised, - 'permissions': permissions, - 'project': project, - 'formset': formset, - 'feature_types': FeatureType.objects.filter(project=project) - } - return render(request, 'geocontrib/project/project_members.html', context) + else: + logger.error(formset.errors) + context = { + "title": "Gestion des membres du projet {}".format(project.title), + 'authorised': authorised, + 'permissions': permissions, + 'project': project, + 'formset': formset, + 'feature_types': FeatureType.objects.filter(project=project) + } + return render(request, 'geocontrib/project/project_members.html', context) ###################### diff --git a/plugin_georchestra/README.md b/plugin_georchestra/README.md index 9a2d9c7c..8856d228 100644 --- a/plugin_georchestra/README.md +++ b/plugin_georchestra/README.md @@ -62,7 +62,8 @@ rôle de contributeur pour le projet. Néanmoins un utilisateur ayant déjà le * les utilisateurs appartenant aux groupes paramétrés dans le champ "Groupes LDAP des administrateurs" du projet (cf. interface d'administrration Django de GéoContrib dans le formulaire du projet) recoivent automatiquement le rôle d'administrateur du projet. - +* les utilisateurs qui ne font pas partis des groupes LDAP mentionnés dans l'interface d'admin, et auxquels des rôles +ont été attribués depuis l'onglet "Membres" d'un projet, conservent bien leurs rôles après une synchronisation. ## Déploiement et configuration diff --git a/plugin_georchestra/management/commands/georchestra_user_sync.py b/plugin_georchestra/management/commands/georchestra_user_sync.py index 769d7af8..3038840c 100644 --- a/plugin_georchestra/management/commands/georchestra_user_sync.py +++ b/plugin_georchestra/management/commands/georchestra_user_sync.py @@ -186,20 +186,25 @@ def sync_ldap_groups(self, user, row): # On liste les projets pour lesquels l'utilisateur n'est ni membre des groupes 'ldap_project_admin_groups' # ni membre des goupes 'ldap_project_contrib_groups' # Les utilisateurs absent de ces groupes se retrouvent simples "utilisateur connecté" + # sauf si ils ont déja un role defini à posteriori dans geocontrib not_admin_and_not_contrib_qs = Project.objects.exclude(ldap_project_admin_groups__overlap=flattened_groups)\ .exclude(ldap_project_contrib_groups__overlap=flattened_groups) if not_admin_and_not_contrib_qs.exists(): for project in not_admin_and_not_contrib_qs or []: - auth, created = Authorization.objects.update_or_create( + auth, created = Authorization.objects.get_or_create( project=project, user=user, defaults={ 'level': UserLevelPermission.objects.get(user_type_id=choices.LOGGED_USER) } ) - - logger.debug("User '{0}' set as {1}'s Project '{2}' ".format( - user.username, auth.level.user_type_id, project.slug) - ) + if not created: + logger.debug("User '{0}' is already {1}'s Project '{2}' ".format( + user.username, auth.level.user_type_id, project.slug) + ) + else: + logger.debug("User '{0}' set as {1}'s Project '{2}' ".format( + user.username, auth.level.user_type_id, project.slug) + ) def user_update_or_create(self, row): try: