diff --git a/CHANGELOG b/CHANGELOG index de2e06806..19b2bdeba 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,7 +15,7 @@ Agenda: Motions: - New export dialog [#3185]. -- New feature: Personal notes for motions [#3190, #3267]. +- New feature: Personal notes for motions [#3190, #3267, #3404]. - Fixed issue when creating/deleting motion comment fields in the settings [#3187]. - Fixed empty motion comment field in motion update form [#3194]. @@ -80,6 +80,8 @@ Core: - Updated CKEditor to 4.7 [#3375]. - Reduced ckeditor toolbar for inline editing [#3368]. - Added custom translations in config [#3383]. +- Added dynamic webpage title [#3404]. +- Added 'go to top'-link [#3404]. Mediafiles: - Fixed reloading of PDF on page change [#3274]. diff --git a/openslides/agenda/static/js/agenda/site.js b/openslides/agenda/static/js/agenda/site.js index 1a2d7027a..ed8dd12e6 100644 --- a/openslides/agenda/static/js/agenda/site.js +++ b/openslides/agenda/static/js/agenda/site.js @@ -391,14 +391,19 @@ angular.module('OpenSlidesApp.agenda.site', [ 'itemId', 'Projector', 'ProjectionDefault', + 'gettextCatalog', + 'WebpageTitle', 'ErrorMessage', - function ($scope, $filter, Agenda, itemId, Projector, ProjectionDefault, ErrorMessage) { + function ($scope, $filter, Agenda, itemId, Projector, ProjectionDefault, gettextCatalog, WebpageTitle, + ErrorMessage) { $scope.alert = {}; $scope.$watch(function () { return Agenda.lastModified(itemId); }, function () { $scope.item = Agenda.get(itemId); + WebpageTitle.updateTitle(gettextCatalog.getString('List of speakers') + ' ' + + gettextCatalog.getString('of') + ' ' + $scope.item.getTitle()); // all speakers $scope.speakers = $filter('orderBy')($scope.item.speakers, 'weight'); // next speakers diff --git a/openslides/assignments/static/js/assignments/base.js b/openslides/assignments/static/js/assignments/base.js index e43cae127..d9b4c3bc7 100644 --- a/openslides/assignments/static/js/assignments/base.js +++ b/openslides/assignments/static/js/assignments/base.js @@ -406,7 +406,10 @@ angular.module('OpenSlidesApp.assignments', []) return isProjectedIds; }, isRelatedProjected: function () { - var listOfSpeakers = this.agenda_item.isListOfSpeakersProjected(); + var listOfSpeakers = []; + if (this.agenda_item) { + listOfSpeakers = this.agenda_item.isListOfSpeakersProjected(); + } return listOfSpeakers.concat(this.isProjected(null, true)); }, }, diff --git a/openslides/assignments/static/js/assignments/site.js b/openslides/assignments/static/js/assignments/site.js index 14d9f9b74..b13223ad9 100644 --- a/openslides/assignments/static/js/assignments/site.js +++ b/openslides/assignments/static/js/assignments/site.js @@ -418,10 +418,11 @@ angular.module('OpenSlidesApp.assignments.site', [ 'gettextCatalog', 'AssignmentPhases', 'AssignmentPdfExport', + 'WebpageTitle', 'ErrorMessage', function($scope, $http, $filter, $timeout, filterFilter, gettext, ngDialog, AssignmentForm, operator, Assignment, User, assignmentId, Projector, ProjectionDefault, gettextCatalog, AssignmentPhases, - AssignmentPdfExport, ErrorMessage) { + AssignmentPdfExport, WebpageTitle, ErrorMessage) { User.bindAll({}, $scope, 'users'); var assignment = Assignment.get(assignmentId); Assignment.loadRelations(assignment, 'agenda_item'); @@ -447,6 +448,7 @@ angular.module('OpenSlidesApp.assignments.site', [ $scope.activeTab = $scope.assignment.polls.length - 1; updateBallotTabsFlag = false; } + WebpageTitle.updateTitle(gettextCatalog.getString('Election') + ' ' + $scope.assignment.title); }); $scope.candidateSelectBox = {}; $scope.phases = AssignmentPhases; diff --git a/openslides/core/static/css/app.css b/openslides/core/static/css/app.css index 15695ddae..b14789157 100644 --- a/openslides/core/static/css/app.css +++ b/openslides/core/static/css/app.css @@ -296,6 +296,24 @@ img { color: #555; } +/** Goto top link **/ +#goto-top { + position: fixed; + bottom: 15px; + right: 30px; + padding: 10px 30px; + background: white; + opacity: 0.6; + transition: opacity 250ms ease-out; + z-index: 100; +} +#goto-top:hover { + opacity: 1; + transition: opacity 250ms ease-in; +} +#goto-top a:hover { + text-decoration: none; +} /** Content **/ diff --git a/openslides/core/static/js/core/pdf.js b/openslides/core/static/js/core/pdf.js index 9382a6296..9789cf3ac 100644 --- a/openslides/core/static/js/core/pdf.js +++ b/openslides/core/static/js/core/pdf.js @@ -818,7 +818,7 @@ angular.module('OpenSlidesApp.core.pdf', []) // If this element is inside a list (happens if copied from word), do not set spaces // and margins. Just leave the paragraph there.. if (!isInsideAList(element)) { - currentParagraph.margin = [20, 0, 0, 0]; + currentParagraph.margin = [0, 0, 0, 0]; if (classes.indexOf('os-split-before') === -1) { currentParagraph.margin[1] = 8; } diff --git a/openslides/core/static/js/core/site.js b/openslides/core/static/js/core/site.js index 1c053357d..669a97aac 100644 --- a/openslides/core/static/js/core/site.js +++ b/openslides/core/static/js/core/site.js @@ -134,20 +134,30 @@ angular.module('OpenSlidesApp.core.site', [ } ]) -// Set up the activeAppTitle for the title from the webpage +.factory('WebpageTitle', [ + '$rootScope', + function ($rootScope) { + $rootScope.activeAppTitle = ''; + return { + updateTitle: function (text) { + $rootScope.activeAppTitle = text || ''; + }, + }; + } +]) + +// Watch for the basePerm on a stateChange and initialize the WebpageTitle factory .run([ '$rootScope', - 'gettextCatalog', 'operator', - function ($rootScope, gettextCatalog, operator) { - $rootScope.activeAppTitle = ''; + 'WebpageTitle', + function ($rootScope, operator, WebpageTitle) { $rootScope.$on('$stateChangeSuccess', function(event, toState) { + WebpageTitle.updateTitle(toState.data ? toState.data.title : ''); if (toState.data) { - $rootScope.activeAppTitle = toState.data.title || ''; $rootScope.baseViewPermissionsGranted = toState.data.basePerm ? operator.hasPerms(toState.data.basePerm) : true; } else { - $rootScope.activeAppTitle = ''; $rootScope.baseViewPermissionsGranted = true; } }); @@ -987,6 +997,23 @@ angular.module('OpenSlidesApp.core.site', [ } ]) +.controller('GotoTopCtrl', [ + '$scope', + '$window', + '$timeout', + function ($scope, $window, $timeout) { + $scope.show = false; + angular.element($window).bind('scroll', function () { + $timeout(function () { + $scope.show = ($window.pageYOffset >= 150); + }); + }); + $scope.gotoTop = function () { + $window.scrollTo(0, 0); + }; + } +]) + // Projector Sidebar Controller .controller('ProjectorSidebarCtrl', [ '$scope', diff --git a/openslides/core/static/templates/index.html b/openslides/core/static/templates/index.html index 70fd63a89..f2a2080c7 100644 --- a/openslides/core/static/templates/index.html +++ b/openslides/core/static/templates/index.html @@ -4,7 +4,7 @@ OpenSlides -OpenSlides – {{ activeAppTitle | translate }} +{{ activeAppTitle | translate }} – OpenSlides @@ -258,6 +258,14 @@ +
+
+ + + +
+
+ diff --git a/openslides/motions/static/js/motions/motion-block.js b/openslides/motions/static/js/motions/motion-block.js index 818bf13e9..b871ecc20 100644 --- a/openslides/motions/static/js/motions/motion-block.js +++ b/openslides/motions/static/js/motions/motion-block.js @@ -144,9 +144,18 @@ angular.module('OpenSlidesApp.motions.motionBlock', []) 'motionBlockId', 'Projector', 'ProjectionDefault', + 'WebpageTitle', + 'gettextCatalog', 'ErrorMessage', - function($scope, $http, ngDialog, Motion, MotionBlockForm, MotionBlock, motionBlockId, Projector, ProjectionDefault, ErrorMessage) { - MotionBlock.bindOne(motionBlockId, $scope, 'motionBlock'); + function($scope, $http, ngDialog, Motion, MotionBlockForm, MotionBlock, motionBlockId, Projector, + ProjectionDefault, WebpageTitle, gettextCatalog, ErrorMessage) { + $scope.$watch(function () { + return MotionBlock.lastModified(motionBlockId); + }, function () { + $scope.motionBlock = MotionBlock.get(motionBlockId); + WebpageTitle.updateTitle(gettextCatalog.getString('Motion block') + ' ' + + $scope.motionBlock.agenda_item.getTitle()); + }); Motion.bindAll({}, $scope, 'motions'); $scope.$watch(function () { return Projector.lastModified(); diff --git a/openslides/motions/static/js/motions/motion-services.js b/openslides/motions/static/js/motions/motion-services.js index 173a188a6..4c0ffb55b 100644 --- a/openslides/motions/static/js/motions/motion-services.js +++ b/openslides/motions/static/js/motions/motion-services.js @@ -134,7 +134,7 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', editors: [] }; var options = Editor.getOptions('inline', 'YOffset'); - _.forEach($scope.noSpecialCommentsFields, function (field, id) { + _.forEachRight($scope.noSpecialCommentsFields, function (field, id) { var inlineEditing = MotionInlineEditing.createInstance($scope, motion, 'view-original-comment-inline-editor-' + id, false, options, function (obj) { diff --git a/openslides/motions/static/js/motions/pdf.js b/openslides/motions/static/js/motions/pdf.js index e72c67e8a..6c92668ae 100644 --- a/openslides/motions/static/js/motions/pdf.js +++ b/openslides/motions/static/js/motions/pdf.js @@ -121,7 +121,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) text: gettextCatalog.getString('Category') + ':', style: ['bold', 'grey'] }, { - text: motion.category.name, + text: motion.category.prefix + ' - ' + motion.category.name, style: 'grey' } ]); @@ -484,7 +484,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) text: gettextCatalog.getString('Category') + ':', style: ['bold', 'grey'] }, { - text: motion.category.name, + text: motion.category.prefix + ' - ' + motion.category.name, style: 'grey' } ]); diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js index a7babaad1..7cff82546 100644 --- a/openslides/motions/static/js/motions/site.js +++ b/openslides/motions/static/js/motions/site.js @@ -902,6 +902,7 @@ angular.module('OpenSlidesApp.motions.site', [ motion.personalNote = PersonalNoteManager.getNote(motion); // For filtering, we cannot filter for .personalNote.star motion.star = motion.personalNote ? motion.personalNote.star : false; + motion.hasPersonalNote = motion.personalNote ? !!motion.personalNote.note : false; if (motion.star === undefined) { motion.star = false; } @@ -967,9 +968,13 @@ angular.module('OpenSlidesApp.motions.site', [ $scope.filter.booleanFilters = { isFavorite: { value: undefined, - displayName: gettext('Favorite'), - choiceYes: gettext('Is favorite'), - choiceNo: gettext('Is not favorite'), + choiceYes: gettext('Marked as favorite'), + choiceNo: gettext('Not marked as favorite'), + }, + hasPersonalNote: { + value: undefined, + choiceYes: gettext('Personal note set'), + choiceNo: gettext('Personal note not set'), }, }; } @@ -1213,12 +1218,13 @@ angular.module('OpenSlidesApp.motions.site', [ 'MotionBlock', 'MotionPdfExport', 'PersonalNoteManager', + 'WebpageTitle', 'EditingWarning', function($scope, $http, $timeout, operator, ngDialog, gettextCatalog, MotionForm, ChangeRecommmendationCreate, ChangeRecommmendationView, MotionChangeRecommendation, Motion, MotionComment, Category, Mediafile, Tag, User, Workflow, Config, motionId, MotionInlineEditing, MotionCommentsInlineEditing, Editor, Projector, ProjectionDefault, MotionBlock, MotionPdfExport, - PersonalNoteManager, EditingWarning) { + PersonalNoteManager, WebpageTitle, EditingWarning) { var motion = Motion.get(motionId); Category.bindAll({}, $scope, 'categories'); Mediafile.bindAll({}, $scope, 'mediafiles'); @@ -1254,6 +1260,13 @@ angular.module('OpenSlidesApp.motions.site', [ $scope.motion = Motion.get(motionId); MotionComment.populateFields($scope.motion); $scope.motion.personalNote = PersonalNoteManager.getNote($scope.motion); + + var webpageTitle = gettextCatalog.getString('Motion') + ' '; + if ($scope.motion.identifier) { + webpageTitle += $scope.motion.identifier + ' - '; + } + webpageTitle += $scope.motion.getTitle(); + WebpageTitle.updateTitle(webpageTitle); }); $scope.projectionModes = [ {mode: 'original', @@ -1537,7 +1550,7 @@ angular.module('OpenSlidesApp.motions.site', [ if (editingStoppedCallback) { editingStoppedCallback(); } - if ($scope.motion.getReason($scope.version)) { + if ($scope.motion && $scope.motion.getReason($scope.version)) { $scope.reasonInlineEditing.disable(); } $scope.inlineEditing.disable(); diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index ccb39a83f..50ba83ddb 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -238,13 +238,13 @@
  • - {{ category.name }} + {{ category.prefix }} – {{ category.name }}
  • - {{ motion.category.name }} + {{ motion.category.prefix }} – {{ motion.category.name }}

    Motion block

    @@ -424,6 +424,13 @@ New vote + +
    + Personal note + + + +
    diff --git a/openslides/motions/static/templates/motions/motion-list.html b/openslides/motions/static/templates/motions/motion-list.html index a5111d184..d978189ee 100644 --- a/openslides/motions/static/templates/motions/motion-list.html +++ b/openslides/motions/static/templates/motions/motion-list.html @@ -86,7 +86,7 @@ @@ -162,7 +162,7 @@
  • - + done
  • @@ -232,7 +232,7 @@
  • - {{ category.name }} + {{ category.prefix }} – {{ category.name }}
  • @@ -292,26 +292,39 @@ - - - + + - {{ booleanFilter.displayName | translate }} + Private - @@ -623,7 +637,7 @@
    - {{ motion.category.name }} + {{ motion.category.prefix }} – {{ motion.category.name }}
    diff --git a/openslides/topics/static/js/topics/site.js b/openslides/topics/static/js/topics/site.js index 3dd69d2ea..6bb13ba94 100644 --- a/openslides/topics/static/js/topics/site.js +++ b/openslides/topics/static/js/topics/site.js @@ -155,8 +155,17 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides 'topicId', 'Projector', 'ProjectionDefault', - function($scope, ngDialog, TopicForm, Topic, topicId, Projector, ProjectionDefault) { - Topic.bindOne(topicId, $scope, 'topic'); + 'WebpageTitle', + 'gettextCatalog', + function($scope, ngDialog, TopicForm, Topic, topicId, Projector, ProjectionDefault, WebpageTitle, + gettextCatalog) { + $scope.$watch(function () { + return Topic.lastModified(topicId); + }, function () { + $scope.topic = Topic.get(topicId); + WebpageTitle.updateTitle(gettextCatalog.getString('Topic') + ' ' + + $scope.topic.agenda_item.getTitle()); + }); $scope.$watch(function () { return Projector.lastModified(); }, function () { diff --git a/openslides/users/static/js/users/site.js b/openslides/users/static/js/users/site.js index cb42fc0d1..84f8fdcbb 100644 --- a/openslides/users/static/js/users/site.js +++ b/openslides/users/static/js/users/site.js @@ -749,9 +749,17 @@ angular.module('OpenSlidesApp.users.site', [ 'Group', 'Projector', 'ProjectionDefault', - function($scope, ngDialog, UserForm, User, userId, Group, Projector, ProjectionDefault) { - User.bindOne(userId, $scope, 'user'); + 'gettextCatalog', + 'WebpageTitle', + function($scope, ngDialog, UserForm, User, userId, Group, Projector, ProjectionDefault, gettextCatalog, + WebpageTitle) { Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups'); + $scope.$watch(function () { + return User.lastModified(userId); + }, function () { + $scope.user = User.get(userId); + WebpageTitle.updateTitle(gettextCatalog.getString('Participant') + ' ' + $scope.user.get_short_name()); + }); $scope.$watch(function () { return Projector.lastModified(); }, function () {