Personal note (client side)
This commit is contained in:
parent
6aee27e49f
commit
96899f63cc
@ -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,7 +92,7 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions',
|
||||
};
|
||||
|
||||
obj.save = function () {
|
||||
saveData(obj);
|
||||
if (!saveData(obj)) {
|
||||
obj.disable();
|
||||
|
||||
Motion.inject(motion);
|
||||
@ -109,6 +116,7 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions',
|
||||
$scope.alert = {type: 'danger', msg: message, show: true};
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return obj;
|
||||
|
@ -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;
|
||||
|
@ -59,7 +59,12 @@
|
||||
<translate>PDF</translate>
|
||||
</a>
|
||||
</div>
|
||||
<h1>{{ motion.agenda_item.getTitle() || motion.getTitle() }}</h1>
|
||||
<h1>
|
||||
{{ motion.agenda_item.getTitle() || motion.getTitle() }}
|
||||
<i class="fa pointer" ng-class="motion.personalNote.star ? 'fa-star' : 'fa-star-o'"
|
||||
ng-if="operator.user"
|
||||
title="{{ 'Set as favorite' | translate }}" ng-click="toggleStar()"></i>
|
||||
</h1>
|
||||
<h2>
|
||||
<translate>Motion</translate> {{ motion.identifier }}
|
||||
<span ng-if="parent">
|
||||
@ -542,3 +547,6 @@
|
||||
|
||||
<!-- Comments section -->
|
||||
<ng-include src="'static/templates/motions/motion-detail/comments.html'"></ng-include>
|
||||
|
||||
<!-- Personal note section -->
|
||||
<ng-include src="'static/templates/motions/motion-detail/personal-note.html'" ng-if="operator.user"></ng-include>
|
||||
|
@ -0,0 +1,36 @@
|
||||
<div class="details">
|
||||
<div class="row">
|
||||
<!-- inline editing toolbar -->
|
||||
<div class="motion-toolbar">
|
||||
<div class="pull-right inline-editing-activator">
|
||||
<button ng-if="!personalNoteInlineEditing.active" ng-click="personalNoteInlineEditing.enable()"
|
||||
class="btn btn-sm btn-default">
|
||||
<i class="fa fa-pencil-square-o"></i>
|
||||
<translate>Inline editing</translate>
|
||||
</button>
|
||||
<button ng-if="personalNoteInlineEditing.active" ng-click="personalNoteInlineEditing.disable()"
|
||||
class="btn btn-sm btn-default">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<translate>Inline editing</translate>
|
||||
</button>
|
||||
</div>
|
||||
<h3 class="toolbar-left" translate>Personal note</h3>
|
||||
</div>
|
||||
|
||||
<!-- personal note editor field -->
|
||||
<div class="col-sm-12">
|
||||
<div id="personal-note-inline-editor" style="min-height: 14px;"
|
||||
ng-bind-html="motion.personalNote.note | trusted" contenteditable="{{ personalNoteInlineEditing.isEditable }}"></div>
|
||||
<!-- save toolbar -->
|
||||
<div class="motion-save-toolbar" ng-class="{ 'visible': (personalNoteInlineEditing.changed && personalNoteInlineEditing.active) }">
|
||||
<div class="changed-hint" translate>The personal note has been changed.</div>
|
||||
<button type="button" ng-click="personalNoteInlineEditing.save()" class="btn btn-primary" translate>
|
||||
Save
|
||||
</button>
|
||||
<button type="button" ng-click="personalNoteInlineEditing.revert()" class="btn btn-default" translate>
|
||||
Revert
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -268,6 +268,30 @@
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<!-- boolean Filters -->
|
||||
<span ng-repeat="(name, booleanFilter) in filter.booleanFilters"
|
||||
ng-if="operator.user.id" uib-dropdown>
|
||||
<span class="pointer" id="dropdown{{ name }}" uib-dropdown-toggle
|
||||
ng-class="{'bold': booleanFilter.value !== undefined, 'disabled': isSelectMode}"
|
||||
ng-disabled="isSelectMode">
|
||||
{{ booleanFilter.displayName | translate }}
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown{{ name }}">
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value ? undefined : true); filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === true}"></i>
|
||||
{{ booleanFilter.choiceYes | translate }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value === false) ? undefined : false; filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === false}"></i>
|
||||
{{ booleanFilter.choiceNo | translate }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<!-- dropdown sort -->
|
||||
<span uib-dropdown>
|
||||
<span class="pointer" id="dropdownSort" uib-dropdown-toggle
|
||||
@ -387,6 +411,17 @@
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<translate>No tag set</translate>
|
||||
</span>
|
||||
<!-- for all boolean Filters -->
|
||||
<span ng-repeat="(name, booleanFilter) in filter.booleanFilters"
|
||||
ng-hide="booleanFilter.value === undefined"
|
||||
class="pointer spacer-left-lg"
|
||||
ng-click="booleanFilter.value = undefined; filter.save();"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
{{ booleanFilter.value ? booleanFilter.choiceYes : booleanFilter.choiceNo | translate }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -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">
|
||||
@ -428,6 +464,10 @@
|
||||
<!-- ID and title -->
|
||||
<div>
|
||||
<a class="title" ui-sref="motions.motion.detail({id: motion.id})">{{ motion.getTitle() }}</a>
|
||||
<a href="" ng-click="toggleStar(motion)">
|
||||
<i class="fa" ng-class="motion.personalNote.star ? 'fa-star' : 'fa-star-o'"
|
||||
title="{{ 'Set as favorite' | translate }}" ng-if="(motion.personalNote.star || motion.hover) && operator.user"></i>
|
||||
</a>
|
||||
<i class="fa fa-paperclip" ng-if="motion.attachments_id.length > 0"></i>
|
||||
</div>
|
||||
<!-- state -->
|
||||
|
@ -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:
|
||||
|
@ -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'];
|
||||
|
Loading…
Reference in New Issue
Block a user