diff --git a/openslides/motions/static/js/motions/motion-services.js b/openslides/motions/static/js/motions/motion-services.js index cb9fd768e..3446a5e54 100644 --- a/openslides/motions/static/js/motions/motion-services.js +++ b/openslides/motions/static/js/motions/motion-services.js @@ -4,6 +4,13 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', 'OpenSlidesApp.motions.lineNumbering']) +/* Generic inline editing factory. + * + * getOriginalData: Function that should return the editor data. The editor object is passed. + * saveData: Function that is called whith the editor object as argument. This function + * should prepare the save. If the function returns true, the save process won't be + * continued. Else a patch request is send. + */ .factory('MotionInlineEditing', [ 'Editor', 'Motion', @@ -85,30 +92,31 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', }; obj.save = function () { - saveData(obj); - obj.disable(); + if (!saveData(obj)) { + obj.disable(); - Motion.inject(motion); - // save change motion object on server - Motion.save(motion, {method: 'PATCH'}).then( - function (success) { - if (versioning) { - $scope.showVersion(motion.getVersion(-1)); + Motion.inject(motion); + // save change motion object on server + Motion.save(motion, {method: 'PATCH'}).then( + function (success) { + if (versioning) { + $scope.showVersion(motion.getVersion(-1)); + } + obj.revert(); + }, + function (error) { + // save error: revert all changes by restore + // (refresh) original motion object from server + Motion.refresh(motion); + obj.revert(); + var message = ''; + for (var e in error.data) { + message += e + ': ' + error.data[e] + ' '; + } + $scope.alert = {type: 'danger', msg: message, show: true}; } - obj.revert(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original motion object from server - Motion.refresh(motion); - obj.revert(); - var message = ''; - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - $scope.alert = {type: 'danger', msg: message, show: true}; - } - ); + ); + } }; return obj; diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js index 57d672a5a..8f6baa1bd 100644 --- a/openslides/motions/static/js/motions/site.js +++ b/openslides/motions/static/js/motions/site.js @@ -854,6 +854,7 @@ angular.module('OpenSlidesApp.motions.site', [ '$http', 'gettext', 'gettextCatalog', + 'operator', 'ngDialog', 'MotionForm', 'Motion', @@ -871,10 +872,9 @@ angular.module('OpenSlidesApp.motions.site', [ 'osTableSort', 'MotionExportForm', 'MotionPdfExport', - function($scope, $state, $http, gettext, gettextCatalog, ngDialog, MotionForm, Motion, MotionComment, - Category, Config, Tag, Workflow, User, Agenda, MotionBlock, Projector, + function($scope, $state, $http, gettext, gettextCatalog, operator, ngDialog, MotionForm, Motion, + MotionComment, Category, Config, Tag, Workflow, User, Agenda, MotionBlock, Projector, ProjectionDefault, osTableFilter, osTableSort, MotionExportForm, MotionPdfExport) { - Motion.bindAll({}, $scope, 'motions'); Category.bindAll({}, $scope, 'categories'); MotionBlock.bindAll({}, $scope, 'motionBlocks'); Tag.bindAll({}, $scope, 'tags'); @@ -889,6 +889,18 @@ angular.module('OpenSlidesApp.motions.site', [ $scope.defaultProjectorId = projectiondefault.projector_id; } }); + $scope.$watch(function () { + return Motion.lastModified(); + }, function () { + $scope.motions = Motion.getAll(); + _.forEach($scope.motions, function (motion) { + motion.personalNote = _.find(motion.personal_notes, function (note) { + return note.user_id === operator.user.id; + }); + // For filtering, we cannot filter for .personalNote.star + motion.star = motion.personalNote ? motion.personalNote.star : false; + }); + }); $scope.alert = {}; // collect all states and all recommendations of all workflows @@ -939,6 +951,14 @@ angular.module('OpenSlidesApp.motions.site', [ tag: [], recommendation: [], }; + $scope.filter.booleanFilters = { + isFavorite: { + value: undefined, + displayName: gettext('Favorite'), + choiceYes: gettext('Is favorite'), + choiceNo: gettext('Is not favorite'), + }, + }; } updateStateFilter(); $scope.filter.propertyList = ['identifier', 'origin']; @@ -1062,6 +1082,16 @@ angular.module('OpenSlidesApp.motions.site', [ } $scope.save(motion); }; + $scope.toggleStar = function (motion) { + if (motion.personalNote) { + motion.personalNote.star = !motion.personalNote.star; + } else { + motion.personalNote = {star: true}; + } + $http.put('/rest/motions/motion/' + motion.id + '/set_personal_note/', + motion.personalNote + ); + }; // open new/edit dialog $scope.openDialog = function (motion) { @@ -1198,6 +1228,9 @@ angular.module('OpenSlidesApp.motions.site', [ }, function () { $scope.motion = Motion.get(motionId); MotionComment.populateFields($scope.motion); + $scope.motion.personalNote = _.find($scope.motion.personal_notes, function (note) { + return note.user_id === operator.user.id; + }); }); $scope.projectionModes = [ {mode: 'original', @@ -1419,6 +1452,19 @@ angular.module('OpenSlidesApp.motions.site', [ } return Boolean(isAllowed); }; + // personal note + $scope.toggleStar = function () { + if ($scope.motion.personalNote) { + $scope.motion.personalNote.star = !$scope.motion.personalNote.star; + } else { + $scope.motion.personalNote = {star: true}; + } + $http.put('/rest/motions/motion/' + $scope.motion.id + '/set_personal_note/', + $scope.motion.personalNote + ); + }; + $scope.changePN = function () { + }; // personal note $scope.toggleStar = function () { @@ -1446,6 +1492,25 @@ angular.module('OpenSlidesApp.motions.site', [ } ); $scope.commentsInlineEditing = MotionCommentsInlineEditing.createInstances($scope, motion); + $scope.personalNoteInlineEditing = MotionInlineEditing.createInstance($scope, motion, + 'personal-note-inline-editor', false, + function (obj) { + return motion.personalNote ? motion.personalNote.note : ''; + }, + function (obj) { + if (motion.personalNote) { + motion.personalNote.note = obj.editor.getData(); + } else { + motion.personalNote = {note: obj.editor.getData()}; + } + $http.put('/rest/motions/motion/' + $scope.motion.id + '/set_personal_note/', + motion.personalNote + ); + obj.revert(); + obj.disable(); + return true; // Do not update the motion via patch request. + } + ); // Change recommendation creation functions $scope.createChangeRecommendation = ChangeRecommmendationCreate; diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index 3016bbe77..a04d3d7c2 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -59,7 +59,12 @@ PDF -

{{ motion.agenda_item.getTitle() || motion.getTitle() }}

+

+ {{ motion.agenda_item.getTitle() || motion.getTitle() }} + +

Motion {{ motion.identifier }} @@ -542,3 +547,6 @@ + + + diff --git a/openslides/motions/static/templates/motions/motion-detail/personal-note.html b/openslides/motions/static/templates/motions/motion-detail/personal-note.html new file mode 100644 index 000000000..2033021d7 --- /dev/null +++ b/openslides/motions/static/templates/motions/motion-detail/personal-note.html @@ -0,0 +1,36 @@ +
+
+ +
+
+ + +
+

Personal note

+
+ + +
+
+ +
+
The personal note has been changed.
+ + +
+
+
+
diff --git a/openslides/motions/static/templates/motions/motion-list.html b/openslides/motions/static/templates/motions/motion-list.html index c6039b1c0..d9e3a8947 100644 --- a/openslides/motions/static/templates/motions/motion-list.html +++ b/openslides/motions/static/templates/motions/motion-list.html @@ -268,6 +268,30 @@
+ + + + {{ booleanFilter.displayName | translate }} + + + + No tag set + + + + + {{ booleanFilter.value ? booleanFilter.choiceYes : booleanFilter.choiceNo | translate }} + + @@ -402,6 +437,7 @@ | MultiselectFilter: filter.multiselectFilters.motionBlock : getItemId.motionBlock | MultiselectFilter: filter.multiselectFilters.recommendation : getItemId.recommendation | MultiselectFilter: filter.multiselectFilters.tag : getItemId.tag + | filter: {star: filter.booleanFilters.isFavorite.value} | toArray | orderBy: sort.column : sort.reverse) | limitTo : itemsPerPage : limitBegin"> @@ -427,7 +463,11 @@
- {{ motion.getTitle() }} + {{ motion.getTitle() }} + + +
diff --git a/openslides/users/models.py b/openslides/users/models.py index 4b0b022a8..47b363dc3 100644 --- a/openslides/users/models.py +++ b/openslides/users/models.py @@ -259,6 +259,7 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser): user=self, content_type=ContentType.objects.get_for_model(content_object), object_id=content_object.id, + user_id=self.id, defaults=changes, ) else: diff --git a/openslides/users/static/js/users/site.js b/openslides/users/static/js/users/site.js index fe4289a4f..de84e2bbc 100644 --- a/openslides/users/static/js/users/site.js +++ b/openslides/users/static/js/users/site.js @@ -538,7 +538,6 @@ angular.module('OpenSlidesApp.users.site', [ choiceYes: gettext('Is a committee'), choiceNo: gettext('Is not a committee'), }, - }; } $scope.filter.propertyList = ['first_name', 'last_name', 'title', 'number', 'comment', 'structure_level'];