From 0937fbfcc5ee61e9fa636f3f529f34a5b4d8a146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 21 Nov 2016 13:33:35 +0100 Subject: [PATCH] Add rich text field --- CHANGELOG.md | 3 +- .../modules/admin/project-values.coffee | 5 ++ .../modules/common/custom-field-values.coffee | 17 ++++++ app/locales/taiga/locale-en.json | 1 + ...custom-field-edit-wysiwyg.directive.coffee | 58 +++++++++++++++++++ .../history-custom-attributes.jade | 28 ++++++--- .../custom-attribute-value-edit.jade | 4 ++ .../custom-attribute-value.jade | 11 +++- .../admin/admin-custom-attributes.jade | 4 ++ app/styles/modules/common/custom-fields.scss | 3 + 10 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 app/modules/components/wysiwyg/custom-field-edit-wysiwyg.directive.coffee diff --git a/CHANGELOG.md b/CHANGELOG.md index d452b6f4..88ba03b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,9 @@ ### Features - Contact with the project: if the projects have this module enabled Taiga users can contact them. - Velocity forecasting. Create sprints according to team velocity. -- Remove bower +- Remove bower, now use only npm packages. - Add new wysiwyg editor (like the Medunm editor) with emojis, local storage changes, mentions... +- Add rich text custom fields (with a wysiwyg editor like descreption or comments). ### Misc - Lots of small and not so small bugfixes. diff --git a/app/coffee/modules/admin/project-values.coffee b/app/coffee/modules/admin/project-values.coffee index b7ddd2f2..772ea749 100644 --- a/app/coffee/modules/admin/project-values.coffee +++ b/app/coffee/modules/admin/project-values.coffee @@ -406,6 +406,7 @@ module.directive("tgColorSelection", ColorSelectionDirective) # Custom attributes types (see taiga-back/taiga/projects/custom_attributes/choices.py) TEXT_TYPE = "text" MULTILINE_TYPE = "multiline" +RICHTEXT_TYPE = "richtext" DATE_TYPE = "date" URL_TYPE = "url" @@ -419,6 +420,10 @@ TYPE_CHOICES = [ key: MULTILINE_TYPE, name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_MULTI" }, + { + key: RICHTEXT_TYPE, + name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_RICHTEXT" + }, { key: DATE_TYPE, name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_DATE" diff --git a/app/coffee/modules/common/custom-field-values.coffee b/app/coffee/modules/common/custom-field-values.coffee index b9808cbb..3b164981 100644 --- a/app/coffee/modules/common/custom-field-values.coffee +++ b/app/coffee/modules/common/custom-field-values.coffee @@ -32,6 +32,7 @@ module = angular.module("taigaCommon") # Custom attributes types (see taiga-back/taiga/projects/custom_attributes/choices.py) TEXT_TYPE = "text" +RICHTEXT_TYPE = "url" MULTILINE_TYPE = "multiline" DATE_TYPE = "date" URL_TYPE = "url" @@ -53,6 +54,10 @@ TYPE_CHOICES = [ { key: URL_TYPE, name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_URL" + }, + { + key: RICHTEXT_TYPE, + name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_RICHTEXT" } ] @@ -193,6 +198,15 @@ CustomAttributeValueDirective = ($template, $selectedText, $compile, $translate, requiredEditionPerm = $attrs.requiredEditionPerm return permissions.indexOf(requiredEditionPerm) > -1 + $scope.saveCustomRichText = (markdown, callback) => + attributeValue.value = markdown + $ctrl.updateAttributeValue(attributeValue).then -> + callback() + render(attributeValue, false) + + $scope.cancelCustomRichText= () => + render(attributeValue, false) + submit = debounce 2000, (event) => event.preventDefault() @@ -214,6 +228,9 @@ CustomAttributeValueDirective = ($template, $selectedText, $compile, $translate, # Bootstrap attributeValue = $scope.$eval($attrs.tgCustomAttributeValue) + if attributeValue.value == null or attributeValue.value == undefined + attributeValue.value = "" + $scope.customAttributeValue = attributeValue render(attributeValue) ## Actions (on view mode) diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index c82506d2..ac8d9fef 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -573,6 +573,7 @@ "ISSUE_DESCRIPTION": "Issues custom fields", "ISSUE_ADD": "Add a custom field in issues", "FIELD_TYPE_TEXT": "Text", + "FIELD_TYPE_RICHTEXT": "Rich text", "FIELD_TYPE_MULTI": "Multi-line", "FIELD_TYPE_DATE": "Date", "FIELD_TYPE_URL": "Url" diff --git a/app/modules/components/wysiwyg/custom-field-edit-wysiwyg.directive.coffee b/app/modules/components/wysiwyg/custom-field-edit-wysiwyg.directive.coffee new file mode 100644 index 00000000..ec79e6b2 --- /dev/null +++ b/app/modules/components/wysiwyg/custom-field-edit-wysiwyg.directive.coffee @@ -0,0 +1,58 @@ +### +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino Garcia +# Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# File: modules/components/wysiwyg/comment-edit-wysiwyg.directive.coffee +### + +CustomFieldEditWysiwyg = (attachmentsFullService) -> + link = ($scope, $el, $attrs) -> + types = { + userstories: "us", + issues: "issue", + tasks: "task" + } + + uploadFile = (file, cb) -> + return attachmentsFullService.addAttachment($scope.vm.projectId, $scope.vm.comment.comment.id, types[$scope.vm.comment.comment._name], file).then (result) -> + cb(result.getIn(['file', 'name']), result.getIn(['file', 'url'])) + + $scope.uploadFiles = (files, cb) -> + for file in files + uploadFile(file, cb) + + return { + scope: true, + link: link, + template: """ +
+ + +
+ """ + } + +angular.module("taigaComponents") + .directive("tgCustomFieldEditWysiwyg", ["tgAttachmentsFullService", CustomFieldEditWysiwyg]) diff --git a/app/modules/history/history/history-templates/history-custom-attributes.jade b/app/modules/history/history/history-templates/history-custom-attributes.jade index 69d6a1bf..d8ba2873 100644 --- a/app/modules/history/history/history-templates/history-custom-attributes.jade +++ b/app/modules/history/history/history-templates/history-custom-attributes.jade @@ -1,19 +1,29 @@ -.diff-custom-new( +.diff-status-wrapper( ng-if="vm.diff.new.length" ng-repeat="newCustom in vm.diff.new" ) span.key(translate="ACTIVITY.CREATED_CUSTOM_ATTRIBUTE") span.diff ({{newCustom.name}}) - span.diff {{newCustom.value}} - -.diff-custom-new( + + span(ng-if="newCustom.type == 'richtext'") + p.diff(tg-bo-html="newCustom.value_diff") + + span(ng-if="newCustom.type != 'richtext'") + span.diff {{newCustom.value}} + +.diff-status-wrapper( ng-if="vm.diff.changed.length" ng-repeat="changeCustom in vm.diff.changed" ) span.key(translate="ACTIVITY.UPDATED_CUSTOM_ATTRIBUTE") span.diff ({{changeCustom.name}}) - span.diff {{changeCustom.changes.value[0]}} - tg-svg( - svg-icon="icon-arrow-right" - ) - span.diff {{changeCustom.changes.value[1]}} + + span(ng-if="changeCustom.type == 'richtext'") + p.diff(tg-bo-html="changeCustom.value_diff") + + span(ng-if="changeCustom.type != 'richtext'") + span.diff {{changeCustom.changes.value[0]}} + tg-svg( + svg-icon="icon-arrow-right" + ) + span.diff {{changeCustom.changes.value[1]}} diff --git a/app/partials/custom-attributes/custom-attribute-value-edit.jade b/app/partials/custom-attributes/custom-attribute-value-edit.jade index e00fc04f..3e2a1a60 100644 --- a/app/partials/custom-attributes/custom-attribute-value-edit.jade +++ b/app/partials/custom-attributes/custom-attribute-value-edit.jade @@ -13,6 +13,8 @@ form.custom-field-single.editable input#custom-field-value(name="value", type="text", value!="<%- value %>") <% } else if (type=="multiline") { %> textarea#custom-field-value(name="value") <%- value %> + <% } else if (type=="richtext") { %> + tg-custom-field-edit-wysiwyg() <% } else if (type=="date") { %> input#custom-field-value(name="value", type="text", data-pikaday, value!="<%- value %>") <% } else if (type=="url") { %> @@ -21,6 +23,8 @@ form.custom-field-single.editable input#custom-field-value(name="value", type="text", value!="<%- value %>") <% } %> + <% if (type != "richtext") { %> div.custom-field-options a.js-save-description(href="", title="{{'COMMON.CUSTOM_ATTRIBUTES.SAVE' | translate}}") tg-svg(svg-icon="icon-save") + <% } %> diff --git a/app/partials/custom-attributes/custom-attribute-value.jade b/app/partials/custom-attributes/custom-attribute-value.jade index 0350a24d..0bd427e5 100644 --- a/app/partials/custom-attributes/custom-attribute-value.jade +++ b/app/partials/custom-attributes/custom-attribute-value.jade @@ -7,14 +7,19 @@ <%- description %> <% } %> + <% if (type=="url") { %> .custom-field-value.js-value-view-mode span - <% if (type=="url") { %> a(href!="<%- value %>") <%- value %> - <% } else { %> + <% } else if (type=="richtext") { %> + .custom-field-value.js-value-view-mode.wysiwyg + div(ng-bind-html!="\'<%- value %>\'|markdownToHTML") + <% } else { %> + .custom-field-value.js-value-view-mode + span <%- value %> - <% } %> + <% } %> <% if (isEditable) { %> .custom-field-options diff --git a/app/partials/includes/modules/admin/admin-custom-attributes.jade b/app/partials/includes/modules/admin/admin-custom-attributes.jade index 847a4133..b2e97952 100644 --- a/app/partials/includes/modules/admin/admin-custom-attributes.jade +++ b/app/partials/includes/modules/admin/admin-custom-attributes.jade @@ -30,6 +30,10 @@ section.custom-fields-table.basic-table ng-switch-default translate="ADMIN.CUSTOM_FIELDS.FIELD_TYPE_TEXT" ) + span( + ng-switch-when="richtext" + translate="ADMIN.CUSTOM_FIELDS.FIELD_TYPE_RICHTEXT" + ) span( ng-switch-when="multiline" translate="ADMIN.CUSTOM_FIELDS.FIELD_TYPE_MULTI" diff --git a/app/styles/modules/common/custom-fields.scss b/app/styles/modules/common/custom-fields.scss index d2aed368..003e9cc0 100644 --- a/app/styles/modules/common/custom-fields.scss +++ b/app/styles/modules/common/custom-fields.scss @@ -71,6 +71,9 @@ padding: 0 1rem 0 2rem; &.js-value-view-mode { white-space: pre-line; + &.wysiwyg { + white-space: normal; + } } } form {