diff --git a/frontend/app/components/inplace-edit/directives/edit-pane/edit-pane.directive.js b/frontend/app/components/inplace-edit/directives/edit-pane/edit-pane.directive.js index fd802ff019..ec4794c928 100644 --- a/frontend/app/components/inplace-edit/directives/edit-pane/edit-pane.directive.js +++ b/frontend/app/components/inplace-edit/directives/edit-pane/edit-pane.directive.js @@ -30,7 +30,7 @@ angular .module('openproject.inplace-edit') .directive('inplaceEditorEditPane', inplaceEditorEditPane); -function inplaceEditorEditPane(EditableFieldsState, FocusHelper, $timeout) { +function inplaceEditorEditPane(EditableFieldsState, FocusHelper, $timeout, $q) { return { transclude: true, replace: true, @@ -48,6 +48,12 @@ function inplaceEditorEditPane(EditableFieldsState, FocusHelper, $timeout) { return field.isRequired(); }; + scope.$on('form.updateRequired', function() { + var submit = $q.defer(); + + scope.editPaneController.updateWorkPackageForm(submit); + }); + scope.$watchCollection('editableFieldsState.workPackage.form', function(form) { var strategy = field.getInplaceEditStrategy(); @@ -117,7 +123,7 @@ function inplaceEditorEditPane(EditableFieldsState, FocusHelper, $timeout) { } }; } -inplaceEditorEditPane.$inject = ['EditableFieldsState', 'FocusHelper', '$timeout']; +inplaceEditorEditPane.$inject = ['EditableFieldsState', 'FocusHelper', '$timeout', '$q']; function InplaceEditorEditPaneController($scope, $element, $location, $timeout, $q, $rootScope, @@ -153,15 +159,42 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout, }); }; + this.handleFailure = function(submit, e) { + setFailure(e); + submit.reject(e); + }; + + this.updateWorkPackageForm = function(submit) { + WorkPackageService.loadWorkPackageForm(EditableFieldsState.workPackage).then( + function(form) { + field.resource.form = form; + EditableFieldsState.workPackage.form = form; + if (_.isEmpty(form.embedded.validationErrors.props)) { + submit.resolve(); + } else { + afterError(); + submit.reject(); + EditableFieldsState.errors = {}; + _.forEach(form.embedded.validationErrors.props, function(error, field) { + if(field === 'startDate' || field === 'dueDate') { + EditableFieldsState.errors['date'] = error.message; + } else { + EditableFieldsState.errors[field] = error.message; + } + }); + + showErrors(); + } + }).catch(vm.handleFailure); + + return submit.promise; + }; + this.submitField = function() { var submit = $q.defer(); var fieldController = $scope.fieldController; var pendingFormChanges = EditableFieldsState.getPendingFormChanges(); var detectedViolations = []; - var handleFailure = function(e) { - setFailure(e); - submit.reject(e); - }; pendingFormChanges[field.name] = field.value; if (vm.editForm.$invalid) { @@ -182,39 +215,22 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout, submit.reject(); } else { fieldController.state.isBusy = true; - WorkPackageService.loadWorkPackageForm(EditableFieldsState.workPackage).then( - function(form) { - EditableFieldsState.workPackage.form = form; - if (_.isEmpty(form.embedded.validationErrors.props)) { - var result = WorkPackageService.updateWorkPackage( - EditableFieldsState.workPackage - ); - result.then(angular.bind(this, function(updatedWorkPackage) { - submit.resolve(); - field.resource = _.extend(field.resource, updatedWorkPackage); - - $scope.$emit('workPackageUpdatedInEditor', updatedWorkPackage); - $scope.$on('workPackageRefreshed', function() { - fieldController.state.isBusy = false; - fieldController.isEditing = false; - }); - uploadPendingAttachments(updatedWorkPackage); - })).catch(handleFailure); - } else { - afterError(); - submit.reject(); - EditableFieldsState.errors = {}; - _.forEach(form.embedded.validationErrors.props, function(error, field) { - if(field === 'startDate' || field === 'dueDate') { - EditableFieldsState.errors['date'] = error.message; - } else { - EditableFieldsState.errors[field] = error.message; - } - }); + vm.updateWorkPackageForm(submit).then(function() { + var result = WorkPackageService.updateWorkPackage( + EditableFieldsState.workPackage + ); + result.then(angular.bind(this, function(updatedWorkPackage) { + submit.resolve(); + field.resource = _.extend(field.resource, updatedWorkPackage); - showErrors(); - } - }).catch(handleFailure); + $scope.$emit('workPackageUpdatedInEditor', updatedWorkPackage); + $scope.$on('workPackageRefreshed', function() { + fieldController.state.isBusy = false; + fieldController.isEditing = false; + }); + uploadPendingAttachments(updatedWorkPackage); + })).catch(vm.handleFailure); + }); } return submit.promise; diff --git a/frontend/app/components/inplace-edit/directives/field-edit/edit-type/edit-type.directive.html b/frontend/app/components/inplace-edit/directives/field-edit/edit-type/edit-type.directive.html new file mode 100644 index 0000000000..67dd14d3c2 --- /dev/null +++ b/frontend/app/components/inplace-edit/directives/field-edit/edit-type/edit-type.directive.html @@ -0,0 +1,15 @@ + diff --git a/frontend/app/components/inplace-edit/directives/field-edit/edit-type/edit-type.directive.js b/frontend/app/components/inplace-edit/directives/field-edit/edit-type/edit-type.directive.js new file mode 100644 index 0000000000..b7ec2e1d06 --- /dev/null +++ b/frontend/app/components/inplace-edit/directives/field-edit/edit-type/edit-type.directive.js @@ -0,0 +1,135 @@ +// -- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See doc/COPYRIGHT.rdoc for more details. +// ++ + +angular + .module('openproject.inplace-edit') + .directive('inplaceEditorType', inplaceEditorType); + +function inplaceEditorType(EditableFieldsState, FocusHelper, WorkPackageService) { + return { + restrict: 'E', + transclude: true, + replace: true, + require: '^workPackageField', + templateUrl: '/components/inplace-edit/directives/field-edit/edit-type/' + + 'edit-type.directive.html', + + controller: InplaceEditorTypeController, + controllerAs: 'customEditorController', + + link: function(scope, element, attrs, fieldController) { + var field = scope.field; + + fieldController.state.isBusy = true; + + scope.emptyField = !scope.field.isRequired(); + + scope.customEditorController.updateAllowedValues(field.name).then(function() { + fieldController.state.isBusy = false; + + if (!EditableFieldsState.forcedEditState) { + EditableFieldsState.editAll.state || FocusHelper.focusUiSelect(element); + } + }); + + scope.$watch('field.value.props', function(newValue, oldValue) { + if (newValue.hrefTracker !== oldValue.hrefTracker) { + scope.$emit('form.updateRequired'); + } + }); + } + }; +} +inplaceEditorType.$inject = ['EditableFieldsState', 'FocusHelper', 'WorkPackageService']; + +function InplaceEditorTypeController($q, $scope, I18n, WorkPackageFieldConfigurationService) { + + this.allowedValues = []; + + this.updateAllowedValues = function(field) { + var customEditorController = this; + + return $q(function(resolve) { + $scope.field.getAllowedValues() + .then(function(values) { + + var sorting = WorkPackageFieldConfigurationService + .getDropdownSortingStrategy(field); + + if (sorting !== null) { + values = _.sortBy(values, sorting); + } + + if (!$scope.field.isRequired()) { + values = addEmptyOption(values); + } + + addHrefTracker(values); + + customEditorController.allowedValues = values; + + resolve(); + }); + }); + }; + + var addEmptyOption = function(values) { + var emptyOption = { props: { href: null, + name: $scope.field.placeholder } }; + + if (!$scope.field.isRequired()) { + var arrayWithEmptyOption = [emptyOption.props]; + + values = arrayWithEmptyOption.concat(values); + + if ($scope.field.value === null) { + $scope.field.value = emptyOption; + } + } + + return values; + }; + + // We have to maintain a separate property just to track the object by + // in the template. This is due to angular aparently not being able to + // track correclty with a field having null as it's value. It does work for + // 'null' (String) however. + var addHrefTracker = function(values) { + _.forEach(values, function(value) { + value.hrefTracker = String(value.href); + }); + + $scope.field.value.props.hrefTracker = String($scope.field.value.props.href); + }; +} + +InplaceEditorTypeController.$inject = [ + '$q', + '$scope', + 'I18n', + 'WorkPackageFieldConfigurationService']; diff --git a/frontend/app/components/inplace-edit/services/work-package-field.service.js b/frontend/app/components/inplace-edit/services/work-package-field.service.js index af5542efb5..18a5ecb975 100644 --- a/frontend/app/components/inplace-edit/services/work-package-field.service.js +++ b/frontend/app/components/inplace-edit/services/work-package-field.service.js @@ -263,13 +263,15 @@ function WorkPackageFieldService($q, $http, $filter, I18n, WorkPackagesHelper, case 'Duration': inplaceType = 'duration'; break; + case 'Type': + inplaceType = 'type'; + break; case 'StringObject': - case 'Version': case 'User': case 'Status': case 'Priority': case 'Category': - case 'Type': + case 'Version': inplaceType = 'drop-down'; break; } diff --git a/frontend/app/services/work-package-service.js b/frontend/app/services/work-package-service.js index ec33785d77..9edb87229d 100644 --- a/frontend/app/services/work-package-service.js +++ b/frontend/app/services/work-package-service.js @@ -225,7 +225,7 @@ module.exports = function($http, headers: { Accept: 'application/hal+json' }, - data:getPendingChanges(workPackage), + data: getPendingChanges(workPackage), contentType: 'application/json; charset=utf-8' }, force: true}; diff --git a/frontend/app/templates/inplace-edit/edit/fields/type.html b/frontend/app/templates/inplace-edit/edit/fields/type.html new file mode 100644 index 0000000000..fd66fb0984 --- /dev/null +++ b/frontend/app/templates/inplace-edit/edit/fields/type.html @@ -0,0 +1 @@ +