diff --git a/CHANGELOG b/CHANGELOG index e9e28f1c5..e7de2dcdb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ Agenda: Motions: - New export dialog [#3185]. - New feature: Personal notes for motions [#3190, #3267, #3404]. +- New feature: Change recommendations for the title of a motion [#3626]. - Fixed issue when creating/deleting motion comment fields in the settings [#3187]. - Fixed empty motion comment field in motion update form [#3194]. diff --git a/openslides/motions/serializers.py b/openslides/motions/serializers.py index 5fe55f434..0dcdd4031 100644 --- a/openslides/motions/serializers.py +++ b/openslides/motions/serializers.py @@ -280,8 +280,12 @@ class MotionChangeRecommendationSerializer(ModelSerializer): 'text', 'creation_time',) + def is_title_cr(self, data): + return int(data['line_from']) == 0 and int(data['line_to']) == 0 + def validate(self, data): - if 'text' in data: + # Change recommendations for titles are stored as plain-text, thus they don't need to be html-escaped + if 'text' in data and not self.is_title_cr(data): data['text'] = validate_html(data['text']) return data diff --git a/openslides/motions/static/css/motions/_change-recommendation-overview.scss b/openslides/motions/static/css/motions/_change-recommendation-overview.scss index 493530379..7df8e3e50 100644 --- a/openslides/motions/static/css/motions/_change-recommendation-overview.scss +++ b/openslides/motions/static/css/motions/_change-recommendation-overview.scss @@ -4,7 +4,7 @@ border-radius: 3px; margin-bottom: 5px; margin-top: -15px; - padding-top: 5px; + padding: 5px 5px 0 5px; h3 { margin-top: 10px; diff --git a/openslides/motions/static/css/motions/_site.scss b/openslides/motions/static/css/motions/_site.scss index 0b297fa50..8acd730b3 100644 --- a/openslides/motions/static/css/motions/_site.scss +++ b/openslides/motions/static/css/motions/_site.scss @@ -106,16 +106,12 @@ left: 20px; } -.line-numbers-outside .os-line-number.selectable:hover:before, .line-numbers-outside .os-line-number.selected:before { +@mixin addChangeRecommendationBtn { cursor: pointer; content: "\f067"; - display: inline-block; - position: absolute; width: 14px; height: 14px; border-radius: 0.25em; - top: 4px; - left: 43px; font-family: FontAwesome; font-size: 12px; color: white; @@ -124,6 +120,55 @@ background-color: #337ab7; } +.line-numbers-outside .os-line-number.selectable:hover:before, .line-numbers-outside .os-line-number.selected:before { + position: absolute; + top: 4px; + left: 43px; + display: inline-block; + @include addChangeRecommendationBtn(); +} + +.motion-header { + .submenu { + position: relative; + z-index: 2; + } + .motion-title { + position: relative; + z-index: 1; + + // Grab the left padding of the parent element to catch hover-events for the :before element + margin-left: -20px; + padding-left: 20px; + + .change-title { + position: relative; + width: 0; + height: 0; + } + .change-title:before { + position: absolute; + top: 18px; + left: -17px; + @include addChangeRecommendationBtn(); + display: none; + } + &:hover .change-title.selectable:before { + display: block; + } + .title-change-indicator { + background-color: #0333ff; + position: absolute; + width: 4px; + height: 32px; + left: 10px; + top: 5px; + cursor: pointer; + } + } +} + + .tt_change_recommendation_create_help { display: none; max-width: 150px; @@ -190,6 +235,13 @@ } } +.diff-box-title { + margin-bottom: 10px; + .description { + font-weight: bold; + } +} + .motion-text-with-diffs.line-numbers-inline .diff-box, .motion-text-with-diffs.line-numbers-none .diff-box { margin-right: -220px; } diff --git a/openslides/motions/static/js/motions/base.js b/openslides/motions/static/js/motions/base.js index 293d34dd1..a874aa630 100644 --- a/openslides/motions/static/js/motions/base.js +++ b/openslides/motions/static/js/motions/base.js @@ -272,6 +272,22 @@ angular.module('OpenSlidesApp.motions', [ title += this.getTitle(); return title; }, + getTitleWithChanges: function (changeRecommendationMode, versionId) { + var titleChange = this.getTitleChangeRecommendation(versionId); + var title; + if (titleChange) { + if (changeRecommendationMode === "changed") { + title = titleChange.text; + } else if (changeRecommendationMode === 'agreed' && !titleChange.rejected) { + title = titleChange.text; + } else { + title = this.getTitle(); + } + } else { + title = this.getTitle(); + } + return title; + }, getSequentialNumber: function () { var id = this.id + ''; var zeros = Math.max(0, OpenSlidesSettings.MOTION_IDENTIFIER_MIN_DIGITS - id.length); @@ -359,7 +375,7 @@ angular.module('OpenSlidesApp.motions', [ _getTextWithChangeRecommendations: function (versionId, highlight, lineBreaks, statusCompareCb) { var lineLength = Config.get('motions_line_length').value, html = this.getVersion(versionId).text, - changes = this.getChangeRecommendations(versionId, 'DESC'); + changes = this.getTextChangeRecommendations(versionId, 'DESC'); for (var i = 0; i < changes.length; i++) { var change = changes[i]; @@ -405,7 +421,7 @@ angular.module('OpenSlidesApp.motions', [ } break; case 'diff': - var changes = this.getChangeRecommendations(versionId, 'ASC'); + var changes = this.getTextChangeRecommendations(versionId, 'ASC'); text = ''; for (var i = 0; i < changes.length; i++) { text += this.getTextBetweenChangeRecommendations(versionId, (i === 0 ? null : changes[i - 1]), changes[i], highlight); @@ -500,7 +516,7 @@ angular.module('OpenSlidesApp.motions', [ } return foundSomething; }, - getChangeRecommendations: function (versionId, order) { + getTextChangeRecommendations: function (versionId, order) { /* * Returns all change recommendations for this given version, sorted by line * @param versionId @@ -516,8 +532,26 @@ angular.module('OpenSlidesApp.motions', [ orderBy: [ ['line_from', order] ] + }).filter(function(change) { + return change.isTextRecommendation(); }); }, + getTitleChangeRecommendation: function (versionId) { + /** + * Returns the change recommendation affecting the title, or null + * @param versionId + * @returns MotionChangeRecommendation|null + */ + versionId = versionId || this.active_version; + var changes = MotionChangeRecommendation.filter({ + where: { + motion_version_id: versionId, + line_from: 0, + line_to: 0 + } + }); + return (changes.length > 0 ? changes[0] : null); + }, hasAmendments: function () { return DS.filter('motions/motion', {parent_id: this.id}).length > 0; }, @@ -861,6 +895,12 @@ angular.module('OpenSlidesApp.motions', [ saveStatus: function() { this.DSSave(); }, + isTitleRecommendation: function() { + return (this.line_from === 0 && this.line_to === 0); + }, + isTextRecommendation: function() { + return (this.line_from !== 0 || this.line_to !== 0); + }, getDiff: function(motion, version, highlight) { var lineLength = Config.get('motions_line_length').value, html = lineNumberingService.insertLineNumbers(motion.getVersion(version).text, lineLength), diff --git a/openslides/motions/static/js/motions/docx.js b/openslides/motions/static/js/motions/docx.js index 172d3b72b..90a78623a 100644 --- a/openslides/motions/static/js/motions/docx.js +++ b/openslides/motions/static/js/motions/docx.js @@ -49,7 +49,7 @@ angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx']) // motions data.tableofcontents_translation = gettextCatalog.getString('Table of contents'); - data.motions_list = getMotionShortData(motions); + data.motions_list = getMotionShortData(motions, params); data.no_motions = gettextCatalog.getString('No motions available.'); return $q(function (resolve) { @@ -77,11 +77,11 @@ angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx']) return _.orderBy(categories, [sortKey]); }; - var getMotionShortData = function (motions) { + var getMotionShortData = function (motions, params) { return _.map(motions, function (motion) { return { identifier: motion.identifier || '', - title: motion.getTitle(), + title: motion.getTitleWithChanges(params.changeRecommendationMode), }; }); }; @@ -97,6 +97,7 @@ angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx']) var sequential_enabled = Config.get('motions_export_sequential_number').value; // promises for create the actual motion data var promises = _.map(motions, function (motion) { + var title = motion.getTitleWithChanges(params.changeRecommendationMode); var text = params.include.text ? motion.getTextByMode(params.changeRecommendationMode, null, null, false) : ''; var reason = params.include.reason ? motion.getReason() : ''; var comments = getMotionComments(motion, params.includeComments); @@ -114,7 +115,7 @@ angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx']) // Actual data id: motion.id, identifier: motion.identifier || '', - title: motion.getTitle(), + title: title, submitters: params.include.submitters ? _.map(motion.submitters, function (submitter) { return submitter.get_full_name(); }).join(', ') : '', diff --git a/openslides/motions/static/js/motions/motion-services.js b/openslides/motions/static/js/motions/motion-services.js index 0e3e943db..1beb0baf0 100644 --- a/openslides/motions/static/js/motions/motion-services.js +++ b/openslides/motions/static/js/motions/motion-services.js @@ -43,7 +43,7 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', obj.editor = CKEDITOR.inline(selector, ckeditorOptions); obj.editor.on('change', function () { $timeout(function() { - if (obj.editor.getData() != obj.originalHtml) { + if (obj.editor.getData() !== obj.originalHtml) { obj.changed = true; } else { obj.changed = false; @@ -183,11 +183,14 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', .factory('ChangeRecommendationCreate', [ 'ngDialog', - 'ChangeRecommendationForm', - function(ngDialog, ChangeRecommendationForm) { + 'ChangeRecommendationTitleForm', + 'ChangeRecommendationTextForm', + function(ngDialog, ChangeRecommendationTitleForm, ChangeRecommendationTextForm) { var MODE_INACTIVE = 0, MODE_SELECTING_FROM = 1, - MODE_SELECTING_TO = 2; + MODE_SELECTING_TO = 2, + + TITLE_DUMMY_LINE_NUMBER = 0; var obj = { mode: MODE_INACTIVE, @@ -200,7 +203,7 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', var $scope, motion, version; obj._getAffectedLineNumbers = function () { - var changeRecommendations = motion.getChangeRecommendations(version), + var changeRecommendations = motion.getTextChangeRecommendations(version.id), affectedLines = []; for (var i = 0; i < changeRecommendations.length; i++) { var change = changeRecommendations[i]; @@ -211,23 +214,29 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', return affectedLines; }; + // startCreating is called right at the beginning after the users interacts with the text for the first time. + // This ensures all necessary nodes have been initialized obj.startCreating = function () { if (obj.mode > MODE_SELECTING_FROM || !motion.isAllowed('can_manage')) { return; } $(".tt_change_recommendation_create_help").removeClass("opened"); - var $lineNumbers = $(".motion-text-original .os-line-number"); + var $lineNumbers = $(".motion-text-original .os-line-number"), + $title = $(".motion-title .change-title"); if ($lineNumbers.filter(".selectable").length === 0) { obj.mode = MODE_SELECTING_FROM; var alreadyAffectedLines = obj._getAffectedLineNumbers(); $lineNumbers.each(function () { var $this = $(this), lineNumber = $this.data("line-number"); - if (alreadyAffectedLines.indexOf(lineNumber) == -1) { + if (alreadyAffectedLines.indexOf(lineNumber) === -1) { $(this).addClass("selectable"); } }); + if (alreadyAffectedLines.indexOf(TITLE_DUMMY_LINE_NUMBER) === -1) { + $title.addClass("selectable"); + } } }; @@ -253,7 +262,7 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', $(".motion-text-original .os-line-number").each(function () { var $this = $(this); if ($this.data("line-number") >= line && !foundCollission) { - if (alreadyAffectedLines.indexOf($this.data("line-number")) == -1) { + if (alreadyAffectedLines.indexOf($this.data("line-number")) === -1) { $(this).addClass("selectable"); } else { $(this).removeClass("selectable"); @@ -268,18 +277,22 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', $(".tt_change_recommendation_create_help").css("top", tt_pos).addClass("opened"); }; + obj.titleClicked = function () { + ngDialog.open(ChangeRecommendationTitleForm.getCreateDialog(motion, version)); + + obj.mode = MODE_INACTIVE; + obj.lineFrom = 0; + obj.lineTo = 0; + $(".motion-text-original .os-line-number").removeClass("selected selectable"); + obj.startCreating(); + }; + obj.setToLine = function (line) { if (line < obj.lineFrom) { return; } obj.mode = MODE_INACTIVE; - obj.lineTo = line + 1; - ngDialog.open(ChangeRecommendationForm.getCreateDialog( - motion, - version, - obj.lineFrom, - obj.lineTo - )); + ngDialog.open(ChangeRecommendationTextForm.getCreateDialog(motion, version, obj.lineFrom, line + 1)); obj.lineFrom = 0; obj.lineTo = 0; @@ -288,19 +301,19 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', }; obj.lineClicked = function (ev) { - if (obj.mode == MODE_INACTIVE) { + if (obj.mode === MODE_INACTIVE) { return; } - if (obj.mode == MODE_SELECTING_FROM) { + if (obj.mode === MODE_SELECTING_FROM) { obj.setFromLine($(ev.target).data("line-number")); $(ev.target).addClass("selected"); - } else if (obj.mode == MODE_SELECTING_TO) { + } else if (obj.mode === MODE_SELECTING_TO) { obj.setToLine($(ev.target).data("line-number")); } }; obj.mouseOver = function (ev) { - if (obj.mode != MODE_SELECTING_TO) { + if (obj.mode !== MODE_SELECTING_TO) { return; } var hoverLine = $(ev.target).data("line-number"); @@ -316,31 +329,37 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', obj.setVersion = function (_motion, _version) { motion = _motion; - version = _version; + version = motion.getVersion(_version); }; - obj.editDialog = function(change_recommendation) { - ngDialog.open(ChangeRecommendationForm.getEditDialog(change_recommendation)); + obj.editTextDialog = function(change_recommendation) { + ngDialog.open(ChangeRecommendationTextForm.getEditDialog(change_recommendation)); + }; + + obj.editTitleDialog = function(change_recommendation) { + ngDialog.open(ChangeRecommendationTitleForm.getEditDialog(change_recommendation)); }; obj.init = function (_scope, _motion) { $scope = _scope; motion = _motion; - version = $scope.version; + version = motion.getVersion($scope.version); var $content = $("#content"); $content.on("click", ".line-numbers-outside .os-line-number.selectable", obj.lineClicked); + $content.on("click", ".motion-title .change-title.selectable", obj.titleClicked); $content.on("click", obj.cancelCreating); $content.on("mouseover", ".line-numbers-outside .os-line-number.selectable", obj.mouseOver); - $content.on("mouseover", ".motion-text-original", obj.startCreating); + $content.on("mouseover", ".motion-text-original, .motion-title", obj.startCreating); $scope.$watch(function () { return $scope.change_recommendations.length; }, function () { - if (obj.mode == MODE_INACTIVE || obj.mode == MODE_SELECTING_FROM) { + if (obj.mode === MODE_INACTIVE || obj.mode === MODE_SELECTING_FROM) { // Recalculate the affected lines so we cannot select lines affected by a recommendation // that has just been created $(".motion-text-original .os-line-number").removeClass("selected selectable"); + $(".motion-title .change-title").removeClass("selected selectable"); obj.startCreating(); } }); @@ -353,9 +372,10 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', obj.destroy = function () { var $content = $("#content"); $content.off("click", ".line-numbers-outside .os-line-number.selectable", obj.lineClicked); + $content.off("click", ".motion-title .change-title.selectable", obj.titleClicked); $content.off("click", obj.cancelCreating); $content.off("mouseover", ".line-numbers-outside .os-line-number.selectable", obj.mouseOver); - $content.off("mouseover", ".motion-text-original", obj.startCreating); + $content.off("mouseover", ".motion-text-original, .motion-title", obj.startCreating); }; return obj; diff --git a/openslides/motions/static/js/motions/pdf.js b/openslides/motions/static/js/motions/pdf.js index 844366eca..9a2b4ba68 100644 --- a/openslides/motions/static/js/motions/pdf.js +++ b/openslides/motions/static/js/motions/pdf.js @@ -63,10 +63,8 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) // title var identifier = motion.identifier ? ' ' + motion.identifier : ''; - var title = PDFLayout.createTitle( - gettextCatalog.getString('Motion') + identifier + ': ' + - motion.getTitle(motionVersion) - ); + var titlePlain = motion.getTitleWithChanges(params.changeRecommendationMode, motionVersion); + var title = PDFLayout.createTitle(gettextCatalog.getString('Motion') + identifier + ': ' + titlePlain); // subtitle and sequential number var subtitleLines = []; @@ -251,20 +249,26 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) } // summary of change recommendations (for motion diff version only) - if (params.changeRecommendationMode == "diff" && motion.changeRecommendations.length) { + if (params.changeRecommendationMode === "diff" && motion.changeRecommendations.length) { var columnLineNumbers = []; var columnChangeType = []; angular.forEach(_.orderBy(motion.changeRecommendations, ['line_from']), function(change) { - // line numbers column - var line; - if (change.line_from >= change.line_to - 1) { - line = change.line_from; + if (change.isTitleRecommendation()) { + columnLineNumbers.push( + gettextCatalog.getString('Title') + ': ' + ); } else { - line = change.line_from + ' - ' + (change.line_to - 1); + // line numbers column + var line; + if (change.line_from >= change.line_to - 1) { + line = change.line_from; + } else { + line = change.line_from + ' - ' + (change.line_to - 1); + } + columnLineNumbers.push( + gettextCatalog.getString('Line') + ' ' + line + ': ' + ); } - columnLineNumbers.push( - gettextCatalog.getString('Line') + ' ' + line + ': ' - ); // change type column if (change.getType(motion.getVersion(motionVersion).text) === 0) { columnChangeType.push(gettextCatalog.getString("Replacement")); @@ -322,7 +326,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) var motionTitle = function() { if (params.include.text) { return [{ - text: motion.getTitle(motionVersion), + text: titlePlain, style: 'heading3' }]; } @@ -338,10 +342,20 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) } }; + var escapeHtml = function(text) { + return text.replace(/&/, "&").replace(//, ">"); + }; + // motion text (with line-numbers) var motionText = function() { if (params.include.text) { - var motionTextContent = motion.getTextByMode(params.changeRecommendationMode, motionVersion); + var motionTextContent = ''; + var titleChange = motion.getTitleChangeRecommendation(); + if (params.changeRecommendationMode === 'diff' && titleChange) { + motionTextContent += '

' + gettextCatalog.getString('New title') + ': ' + + escapeHtml(titleChange.text) + '

'; + } + motionTextContent += motion.getTextByMode(params.changeRecommendationMode, motionVersion); return converter.convertHTML(motionTextContent, params.lineNumberMode); } }; diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js index b88b4ca21..1b70870c8 100644 --- a/openslides/motions/static/js/motions/site.js +++ b/openslides/motions/static/js/motions/site.js @@ -159,17 +159,88 @@ angular.module('OpenSlidesApp.motions.site', [ } ]) -.factory('ChangeRecommendationForm', [ +.factory('ChangeRecommendationTitleForm', [ 'gettextCatalog', 'Editor', 'Config', - function(gettextCatalog, Editor, Config) { + function(gettextCatalog) { + return { + // ngDialog for motion form + getCreateDialog: function (motion, version) { + return { + template: 'static/templates/motions/change-recommendation-form.html', + controller: 'ChangeRecommendationTitleCreateCtrl', + className: 'ngdialog-theme-default wide-form', + closeByEscape: false, + closeByDocument: false, + resolve: { + motion: function() { + return motion; + }, + version: function() { + return version; + } + } + }; + }, + getEditDialog: function(change) { + return { + template: 'static/templates/motions/change-recommendation-form.html', + controller: 'ChangeRecommendationTitleUpdateCtrl', + className: 'ngdialog-theme-default wide-form', + closeByEscape: false, + closeByDocument: false, + resolve: { + change: function() { + return change; + } + } + }; + }, + // angular-formly fields for motion form + getFormFields: function () { + return [ + { + key: 'identifier', + type: 'input', + templateOptions: { + label: gettextCatalog.getString('Identifier') + }, + hide: true + }, + { + key: 'motion_version_id', + type: 'input', + templateOptions: { + label: gettextCatalog.getString('Motion') + }, + hide: true + }, + { + key: 'text', + type: 'input', + templateOptions: { + label: gettextCatalog.getString('New title'), + required: false + } + } + ]; + } + }; + } +]) + +.factory('ChangeRecommendationTextForm', [ + 'gettextCatalog', + 'Editor', + 'Config', + function(gettextCatalog, Editor) { return { // ngDialog for motion form getCreateDialog: function (motion, version, lineFrom, lineTo) { return { template: 'static/templates/motions/change-recommendation-form.html', - controller: 'ChangeRecommendationCreateCtrl', + controller: 'ChangeRecommendationTextCreateCtrl', className: 'ngdialog-theme-default wide-form', closeByEscape: false, closeByDocument: false, @@ -192,7 +263,7 @@ angular.module('OpenSlidesApp.motions.site', [ getEditDialog: function(change) { return { template: 'static/templates/motions/change-recommendation-form.html', - controller: 'ChangeRecommendationUpdateCtrl', + controller: 'ChangeRecommendationTextUpdateCtrl', className: 'ngdialog-theme-default wide-form', closeByEscape: false, closeByDocument: false, @@ -1362,9 +1433,19 @@ angular.module('OpenSlidesApp.motions.site', [ $scope.$watch(function () { return MotionChangeRecommendation.lastModified(); }, function () { - $scope.change_recommendations = MotionChangeRecommendation.filter({ + $scope.change_recommendations = []; + $scope.title_change_recommendation = null; + MotionChangeRecommendation.filter({ 'where': {'motion_version_id': {'==': motion.active_version}} + }).forEach(function(change) { + if (change.isTextRecommendation()) { + $scope.change_recommendations.push(change); + } + if (change.isTitleRecommendation()) { + $scope.title_change_recommendation = change; + } }); + if ($scope.change_recommendations.length === 0) { $scope.setProjectionMode($scope.projectionModes[0]); } @@ -1399,6 +1480,8 @@ angular.module('OpenSlidesApp.motions.site', [ } webpageTitle += $scope.motion.getTitle(); WebpageTitle.updateTitle(webpageTitle); + + $scope.createChangeRecommendation.setVersion(motion, motion.active_version); }); $scope.projectionModes = [ {mode: 'original', @@ -1783,27 +1866,26 @@ angular.module('OpenSlidesApp.motions.site', [ } ]) -.controller('ChangeRecommendationUpdateCtrl', [ +.controller('ChangeRecommendationTitleUpdateCtrl', [ '$scope', 'MotionChangeRecommendation', - 'ChangeRecommendationForm', + 'ChangeRecommendationTitleForm', 'diffService', 'change', 'ErrorMessage', - function ($scope, MotionChangeRecommendation, ChangeRecommendationForm, diffService, change, ErrorMessage) { + function ($scope, MotionChangeRecommendation, ChangeRecommendationTitleForm, diffService, change, ErrorMessage) { $scope.alert = {}; $scope.model = angular.copy(change); // get all form fields - $scope.formFields = ChangeRecommendationForm.getFormFields(change.line_from, change.line_to); + $scope.formFields = ChangeRecommendationTitleForm.getFormFields(); // save motion $scope.save = function (change) { - change.text = diffService.removeDuplicateClassesInsertedByCkeditor(change.text); // inject the changed change recommendation (copy) object back into DS store MotionChangeRecommendation.inject(change); // save changed change recommendation object on server MotionChangeRecommendation.save(change).then( - function(success) { + function() { $scope.closeThisDialog(); }, function (error) { @@ -1815,22 +1897,87 @@ angular.module('OpenSlidesApp.motions.site', [ } ]) -.controller('ChangeRecommendationCreateCtrl', [ +.controller('ChangeRecommendationTitleCreateCtrl', [ '$scope', 'Motion', 'MotionChangeRecommendation', - 'ChangeRecommendationForm', + 'ChangeRecommendationTitleForm', + 'Config', + 'diffService', + 'motion', + 'version', + function($scope, Motion, MotionChangeRecommendation, ChangeRecommendationTitleForm, Config, diffService, motion, + version) { + $scope.alert = {}; + + $scope.model = { + text: version.title, + motion_version_id: version.id + }; + + // get all form fields + $scope.formFields = ChangeRecommendationTitleForm.getFormFields(); + // save motion + $scope.save = function (change) { + change.line_from = 0; + change.line_to = 0; + MotionChangeRecommendation.create(change).then( + function() { + $scope.closeThisDialog(); + } + ); + }; + } +]) + +.controller('ChangeRecommendationTextUpdateCtrl', [ + '$scope', + 'MotionChangeRecommendation', + 'ChangeRecommendationTextForm', + 'diffService', + 'change', + 'ErrorMessage', + function ($scope, MotionChangeRecommendation, ChangeRecommendationTextForm, diffService, change, ErrorMessage) { + $scope.alert = {}; + $scope.model = angular.copy(change); + + // get all form fields + $scope.formFields = ChangeRecommendationTextForm.getFormFields(change.line_from, change.line_to); + // save motion + $scope.save = function (change) { + change.text = diffService.removeDuplicateClassesInsertedByCkeditor(change.text); + // inject the changed change recommendation (copy) object back into DS store + MotionChangeRecommendation.inject(change); + // save changed change recommendation object on server + MotionChangeRecommendation.save(change).then( + function() { + $scope.closeThisDialog(); + }, + function (error) { + MotionChangeRecommendation.refresh(change); + $scope.alert = ErrorMessage.forAlert(error); + } + ); + }; + } +]) + +.controller('ChangeRecommendationTextCreateCtrl', [ + '$scope', + 'Motion', + 'MotionChangeRecommendation', + 'ChangeRecommendationTextForm', 'Config', 'diffService', 'motion', 'version', 'lineFrom', 'lineTo', - function($scope, Motion, MotionChangeRecommendation, ChangeRecommendationForm, Config, diffService, motion, + function($scope, Motion, MotionChangeRecommendation, ChangeRecommendationTextForm, Config, diffService, motion, version, lineFrom, lineTo) { $scope.alert = {}; - var html = motion.getTextWithLineBreaks(version), + var html = motion.getTextWithLineBreaks(version.id), lineData = diffService.extractRangeByLineNumbers(html, lineFrom, lineTo); $scope.model = { @@ -1838,17 +1985,17 @@ angular.module('OpenSlidesApp.motions.site', [ lineData.html + lineData.innerContextEnd + lineData.outerContextEnd, line_from: lineFrom, line_to: lineTo, - motion_version_id: version, + motion_version_id: version.id, type: 0 }; // get all form fields - $scope.formFields = ChangeRecommendationForm.getFormFields(lineFrom, lineTo); + $scope.formFields = ChangeRecommendationTextForm.getFormFields(lineFrom, lineTo); // save motion $scope.save = function (motion) { motion.text = diffService.removeDuplicateClassesInsertedByCkeditor(motion.text); MotionChangeRecommendation.create(motion).then( - function(success) { + function() { $scope.closeThisDialog(); } ); diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index a409fd438..ac799681f 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -1,4 +1,4 @@ -
+
-

- {{ motion.getTitle() }} - -

-
+ +

+ + + + {{ motion.getTitleWithChanges(viewChangeRecommendations.mode) }} + + +

+ +