diff --git a/Gemfile b/Gemfile index 905ae63c81..ca794e316d 100644 --- a/Gemfile +++ b/Gemfile @@ -126,7 +126,7 @@ gem "prototype-rails" # replace those with :remote => true gem 'prototype_legacy_helper', '0.0.0', :git => 'https://github.com/rails/prototype_legacy_helper.git' -gem 'i18n-js', git: "https://github.com/fnando/i18n-js.git", branch: 'rewrite' +gem 'i18n-js', git: "https://github.com/fnando/i18n-js.git", branch: '12fe8ec2133dc162087eef2b1639309a01cbb414' # small wrapper around the command line gem 'cocaine' diff --git a/Gemfile.lock b/Gemfile.lock index f3aa3e97b7..e2050cd6f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,8 +40,8 @@ GIT GIT remote: https://github.com/fnando/i18n-js.git - revision: 4e5c525ff6e1ec4d3449852746fa5651a0577d68 - branch: rewrite + revision: 12fe8ec2133dc162087eef2b1639309a01cbb414 + branch: 12fe8ec2133dc162087eef2b1639309a01cbb414 specs: i18n-js (3.0.0.rc5) i18n diff --git a/app/assets/javascripts/angular/config/configuration-service.js b/app/assets/javascripts/angular/config/configuration-service.js index 69f5f2fc0b..5846524c65 100644 --- a/app/assets/javascripts/angular/config/configuration-service.js +++ b/app/assets/javascripts/angular/config/configuration-service.js @@ -28,16 +28,9 @@ angular.module('openproject.config') -.constant('DEFAULT_WORK_PACKAGE_PROPERTIES', [ - 'status', 'assignee', 'responsible', - 'date', 'percentageDone', 'priority', - 'estimatedTime', 'versionName' -]) - .service('ConfigurationService', [ '$log', - 'DEFAULT_WORK_PACKAGE_PROPERTIES', - function($log, DEFAULT_WORK_PACKAGE_PROPERTIES) { + function($log) { return { settingsPresent: function() { @@ -94,7 +87,7 @@ angular.module('openproject.config') workPackageAttributes: function() { var attributes = (this.workPackageAttributeSettingsPresent()) ? gon.settings.work_package_attributes : []; - return DEFAULT_WORK_PACKAGE_PROPERTIES.concat(attributes); + return attributes; } }; }]); diff --git a/app/assets/javascripts/angular/directives/components/focus.js b/app/assets/javascripts/angular/directives/components/focus.js index d31177a014..13ad309e4f 100644 --- a/app/assets/javascripts/angular/directives/components/focus.js +++ b/app/assets/javascripts/angular/directives/components/focus.js @@ -29,10 +29,16 @@ // TODO move to UI components angular.module('openproject.uiComponents') -.directive('focus', function() { +.directive('focus', ['$timeout', function($timeout) { return { link: function(scope, element, attrs) { - element[0].focus(); + var condition = (attrs.focus) ? scope.$eval(attrs.focus) : true; + + if (condition) { + $timeout(function() { + element[0].focus(); + }); + } } }; -}); +}]); diff --git a/app/assets/javascripts/angular/directives/components/single-click.js b/app/assets/javascripts/angular/directives/components/single-click.js new file mode 100644 index 0000000000..a0c522be16 --- /dev/null +++ b/app/assets/javascripts/angular/directives/components/single-click.js @@ -0,0 +1,63 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2014 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. +//++ + +// TODO move to UI components +angular.module('openproject.uiComponents') + +.constant('DOUBLE_CLICK_DELAY', 300) + +// Thanks to http://stackoverflow.com/a/20445344 +.directive('singleClick', [ + 'DOUBLE_CLICK_DELAY', + '$parse', + '$timeout', + function(DOUBLE_CLICK_DELAY, $parse, $timeout) { + + return { + restrict: 'A', + link: function(scope, element, attrs) { + var fn = $parse(attrs.singleClick); + var clicks = 0, timer = null; + + if (fn) { + element.on('click', function (event) { + clicks++; //count clicks + if(clicks === 1) { + timer = $timeout(function() { + fn(scope, { $event: event }); + clicks = 0; //after action performed, reset counter + }, DOUBLE_CLICK_DELAY); + } else { + $timeout.cancel(timer); //prevent single-click action + clicks = 0; //after action performed, reset counter + } + }); + } + } + }; +}]); diff --git a/app/assets/javascripts/angular/services/activity-service.js b/app/assets/javascripts/angular/services/activity-service.js index f3e497b79a..f32b24df3a 100644 --- a/app/assets/javascripts/angular/services/activity-service.js +++ b/app/assets/javascripts/angular/services/activity-service.js @@ -37,7 +37,8 @@ angular.module('openproject.services') var options = { ajax: { method: "POST", - data: { comment: comment } + data: JSON.stringify({ comment: comment }), + contentType: "application/json; charset=utf-8" } }; @@ -48,7 +49,8 @@ angular.module('openproject.services') var options = { ajax: { method: 'PATCH', - data: { comment: comment } + data: JSON.stringify({ comment: comment }), + contentType: "application/json; charset=utf-8" } }; diff --git a/app/assets/javascripts/angular/services/hook-service.js b/app/assets/javascripts/angular/services/hook-service.js new file mode 100644 index 0000000000..e88b7a10d6 --- /dev/null +++ b/app/assets/javascripts/angular/services/hook-service.js @@ -0,0 +1,62 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2014 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.services') + +.service('HookService', [function() { + var hooks = { }; + + HookService = { + register: function(id, callback) { + if (callback && typeof(callback) == "function") { + if (!hooks[id]) { + hooks[id] = []; + } + + hooks[id].push(callback); + } + }, + call: function(id, params) { + var results = []; + + if (hooks[id]) { + for (var x = 0; x < hooks[id].length; x++) { + var result = hooks[id][x](params); + + if (result) { + results.push(result); + } + } + } + + return results; + } + }; + + return HookService; +}]); diff --git a/app/assets/javascripts/angular/services/work-package-service.js b/app/assets/javascripts/angular/services/work-package-service.js index 6397bb60a2..925b999878 100644 --- a/app/assets/javascripts/angular/services/work-package-service.js +++ b/app/assets/javascripts/angular/services/work-package-service.js @@ -130,13 +130,28 @@ angular.module('openproject.services') }); }, + updateWorkPackage: function(workPackage, data) { + var options = { ajax: { + method: "PATCH", + headers: { + Accept: "application/hal+json" + }, + data: JSON.stringify(data), + contentType: "application/json; charset=utf-8" + }}; + return workPackage.links.update.fetch(options).then(function(workPackage) { + return workPackage; + }) + }, + addWorkPackageRelation: function(workPackage, toId, relationType) { var options = { ajax: { method: "POST", - data: { + data: JSON.stringify({ to_id: toId, relation_type: relationType - } + }), + contentType: "application/json; charset=utf-8" } }; return workPackage.links.addRelation.fetch(options).then(function(relation){ return relation; diff --git a/app/assets/javascripts/angular/services/work-packages-table-service.js b/app/assets/javascripts/angular/services/work-packages-table-service.js index a53f6b7477..d6c2ad811d 100644 --- a/app/assets/javascripts/angular/services/work-packages-table-service.js +++ b/app/assets/javascripts/angular/services/work-packages-table-service.js @@ -129,6 +129,32 @@ angular.module('openproject.workPackages.services') field: columnName, direction: direction }); + }, + + setCheckedStateForAllRows: function(rows, state) { + angular.forEach(rows, function(row) { + row.checked = state; + }); + }, + setRowSelection: function(row, state) { + row.checked = state; + }, + selectRowRange: function(rows, row, activeSelectionBorderIndex) { + if (WorkPackagesTableHelper.getSelectedRows(rows).length == 0) { + this.setRowSelection(row, true); + + activeSelectionBorderIndex = rows.indexOf(row); + } else { + var index = rows.indexOf(row); + var start = Math.min(index, activeSelectionBorderIndex); + var end = Math.max(index, activeSelectionBorderIndex); + + for (var x = 0; x < rows.length; x++) { + rows[x].checked = x >= start && x <= end; + } + } + + return activeSelectionBorderIndex; } }; diff --git a/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js b/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js index 39e3d149d4..fa19f032bf 100644 --- a/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js +++ b/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js @@ -64,20 +64,21 @@ angular.module('openproject.uiComponents') locals[key] = scope[key]; }); - function toggle() { - active() ? close() : open(); + function toggle(event) { + active() ? close() : open(event); } function active() { return contextMenu.active() && ctrl.opened(); } - function open() { + function open(event) { ctrl.open(); - contextMenu.open(locals) + contextMenu.open(event.target, locals) .then(function(element) { menuElement = element; + angular.element(element).trap(); }); } @@ -96,7 +97,7 @@ angular.module('openproject.uiComponents') event.stopPropagation(); scope.$apply(function() { - toggle(); + toggle(event); }); // set css position parameters after the digest has been completed diff --git a/app/assets/javascripts/angular/work_packages/column-context-menu.js b/app/assets/javascripts/angular/work_packages/column-context-menu.js index 971fbcdac2..6b3bb55b33 100644 --- a/app/assets/javascripts/angular/work_packages/column-context-menu.js +++ b/app/assets/javascripts/angular/work_packages/column-context-menu.js @@ -58,7 +58,6 @@ angular.module('openproject.workPackages') $scope.isGroupable = WorkPackagesTableService.isGroupable($scope.column); }); - // context menu actions $scope.groupBy = function(columnName) { @@ -89,4 +88,42 @@ angular.module('openproject.workPackages') $scope.insertColumns = function() { columnsModal.activate(); }; + + $scope.canSort = function() { + return $scope.column && !!$scope.column.sortable; + }; + + function isValidColumn(column) { + return column && column.name !== 'id'; + } + + $scope.canMoveLeft = function() { + return isValidColumn($scope.column) && $scope.columns.indexOf($scope.column) !== 0; + }; + + $scope.canMoveRight = function() { + return isValidColumn($scope.column) && $scope.columns.indexOf($scope.column) !== $scope.columns.length - 1 + }; + + $scope.canBeHidden = function() { + return isValidColumn($scope.column); + }; + + $scope.focusFeature = function(feature) { + var focus; + var mergeOrReturn = function(currentState, state) { + return ((currentState === undefined) ? state : currentState && !state); + }; + + switch (feature) { + case 'insert': focus = mergeOrReturn(focus, true); + case 'hide': focus = mergeOrReturn(focus, $scope.canBeHidden()); + case 'moveRight': focus = mergeOrReturn(focus, $scope.canMoveRight()); + case 'moveLeft': focus = mergeOrReturn(focus, $scope.canMoveLeft()); + case 'group': focus = mergeOrReturn(focus, !!$scope.isGroupable); + default: focus = mergeOrReturn(focus, $scope.canSort()); + } + + return focus; + } }]); diff --git a/app/assets/javascripts/angular/work_packages/controllers/details-tab-overview-controller.js b/app/assets/javascripts/angular/work_packages/controllers/details-tab-overview-controller.js index 4f5209e44f..d645c4edce 100644 --- a/app/assets/javascripts/angular/work_packages/controllers/details-tab-overview-controller.js +++ b/app/assets/javascripts/angular/work_packages/controllers/details-tab-overview-controller.js @@ -38,8 +38,17 @@ angular.module('openproject.workPackages.controllers') 'CustomFieldHelper', 'WorkPackagesHelper', 'UserService', + 'HookService', '$q', - function($scope, I18n, ConfigurationService, USER_TYPE, CustomFieldHelper, WorkPackagesHelper, UserService, $q) { + function($scope, + I18n, + ConfigurationService, + USER_TYPE, + CustomFieldHelper, + WorkPackagesHelper, + UserService, + HookService, + $q) { // work package properties @@ -111,7 +120,17 @@ angular.module('openproject.workPackages.controllers') index < 6 && secondRowToBeDisplayed()) { addFormattedValueToPresentProperties(property, label, value, format); } else { - $scope.emptyWorkPackageProperties.push(label); + var plugInValues = HookService.call('workPackageOverviewAttributes', + { type: property, + workPackage: $scope.workPackage }); + + if (plugInValues.length == 0) { + $scope.emptyWorkPackageProperties.push(label); + } else { + for (var x = 0; x < plugInValues.length; x++) { + addFormattedValueToPresentProperties(property, label, plugInValues[x], 'dynamic'); + } + } } }); })(); diff --git a/app/assets/javascripts/angular/work_packages/controllers/work-package-details-controller.js b/app/assets/javascripts/angular/work_packages/controllers/work-package-details-controller.js index 6b00ca674a..f4ffaeec2f 100644 --- a/app/assets/javascripts/angular/work_packages/controllers/work-package-details-controller.js +++ b/app/assets/javascripts/angular/work_packages/controllers/work-package-details-controller.js @@ -39,6 +39,7 @@ angular.module('openproject.workPackages.controllers') follows: "Relation::Follows" }) .constant('RELATION_IDENTIFIERS', { + parent: "parent", relatedTo: "relates", duplicates: "duplicates", duplicated: "duplicated", @@ -58,11 +59,13 @@ angular.module('openproject.workPackages.controllers') 'RELATION_IDENTIFIERS', '$q', 'WorkPackagesHelper', + 'PathHelper', + 'UsersHelper', 'ConfigurationService', 'CommonRelationsHandler', 'ChildrenRelationsHandler', 'ParentRelationsHandler', - function($scope, latestTab, workPackage, I18n, VISIBLE_LATEST, RELATION_TYPES, RELATION_IDENTIFIERS, $q, WorkPackagesHelper, ConfigurationService, CommonRelationsHandler, ChildrenRelationsHandler, ParentRelationsHandler) { + function($scope, latestTab, workPackage, I18n, VISIBLE_LATEST, RELATION_TYPES, RELATION_IDENTIFIERS, $q, WorkPackagesHelper, PathHelper, UsersHelper, ConfigurationService, CommonRelationsHandler, ChildrenRelationsHandler, ParentRelationsHandler) { $scope.$on('$stateChangeSuccess', function(event, toState){ latestTab.registerState(toState.name); }); @@ -111,16 +114,19 @@ angular.module('openproject.workPackages.controllers') $scope.activities = displayedActivities($scope.workPackage); // watchers - $scope.watchers = workPackage.embedded.watchers; + + // Author $scope.author = workPackage.embedded.author; + $scope.authorPath = PathHelper.staticUserPath($scope.author.props.id); + $scope.authorActive = UsersHelper.isActive($scope.author); // Attachments $scope.attachments = workPackage.embedded.attachments; // relations $q.all(WorkPackagesHelper.getParent(workPackage)).then(function(parents) { - var relationsHandler = new ParentRelationsHandler(workPackage, parents); + var relationsHandler = new ParentRelationsHandler(workPackage, parents, "parent"); $scope.wpParent = relationsHandler; }); diff --git a/app/assets/javascripts/angular/work_packages/directives/work-package-dynamic-attribute-directive.js b/app/assets/javascripts/angular/work_packages/directives/work-package-dynamic-attribute-directive.js new file mode 100644 index 0000000000..13eee7b5d1 --- /dev/null +++ b/app/assets/javascripts/angular/work_packages/directives/work-package-dynamic-attribute-directive.js @@ -0,0 +1,45 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2014 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.workPackages.directives') + +.directive('workPackageDynamicAttribute', ['$compile', function($compile){ + return { + restrict: 'EA', + scope: { + htmlElement: '=', + workPackage: '=' + }, + link: function(scope, element, attributes) { + var html = '<' + scope.htmlElement + '>' + + element.html(html); + $compile(element.contents())(scope); + } + }; +}]); diff --git a/app/assets/javascripts/angular/work_packages/directives/work-packages-table-directive.js b/app/assets/javascripts/angular/work_packages/directives/work-packages-table-directive.js index 527570b9fe..048b54a9e0 100644 --- a/app/assets/javascripts/angular/work_packages/directives/work-packages-table-directive.js +++ b/app/assets/javascripts/angular/work_packages/directives/work-packages-table-directive.js @@ -33,7 +33,8 @@ angular.module('openproject.workPackages.directives') 'WorkPackagesTableService', 'flags', 'PathHelper', - function(I18n, WorkPackagesTableService, flags, PathHelper){ + '$state', + function(I18n, WorkPackagesTableService, flags, PathHelper, $state){ return { restrict: 'E', @@ -54,6 +55,8 @@ angular.module('openproject.workPackages.directives') updateBackUrl: '=' }, link: function(scope, element, attributes) { + var activeSelectionBorderIndex; + scope.I18n = I18n; scope.workPackagesTableData = WorkPackagesTableService.getWorkPackagesTableData(); scope.workPackagePath = PathHelper.staticWorkPackagePath; @@ -76,9 +79,7 @@ angular.module('openproject.workPackages.directives') }); scope.setCheckedStateForAllRows = function(state) { - angular.forEach(scope.rows, function(row) { - row.checked = state; - }); + WorkPackagesTableService.setCheckedStateForAllRows(scope.rows, state); }; var groupableColumns = WorkPackagesTableService.getGroupableColumns(); @@ -97,6 +98,51 @@ angular.module('openproject.workPackages.directives') }, function(detailsEnabled) { scope.hideWorkPackageDetails = !detailsEnabled; }); + + // Thanks to http://stackoverflow.com/a/880518 + function clearSelection() { + if(document.selection && document.selection.empty) { + document.selection.empty(); + } else if(window.getSelection) { + var sel = window.getSelection(); + sel.removeAllRanges(); + } + } + + function setRowSelectionState(row, selected) { + activeSelectionBorderIndex = scope.rows.indexOf(row); + WorkPackagesTableService.setRowSelection(row, selected); + } + + scope.selectWorkPackage = function(row, $event) { + if ($event.target.type != 'checkbox') { + var currentRowCheckState = row.checked; + var index = scope.rows.indexOf(row); + + if (!($event.ctrlKey || $event.shiftKey)) { + scope.setCheckedStateForAllRows(false); + } + + if ($event.shiftKey) { + clearSelection(); + activeSelectionBorderIndex = WorkPackagesTableService.selectRowRange(scope.rows, row, activeSelectionBorderIndex); + } else { + setRowSelectionState(row, !currentRowCheckState); + } + } + }; + + scope.showWorkPackageDetails = function(row) { + var workPackageState = $state.get('work-packages'); + + clearSelection(); + + scope.setCheckedStateForAllRows(false); + + setRowSelectionState(row, true); + + $state.go(workPackageState.resolve.latestTab().getStateName(), { workPackageId: row.object.id }); + }; } }; }]); diff --git a/app/assets/javascripts/angular/work_packages/view_models/relations-handler.js b/app/assets/javascripts/angular/work_packages/view_models/relations-handler.js index 3d8256000d..a17bdee9eb 100644 --- a/app/assets/javascripts/angular/work_packages/view_models/relations-handler.js +++ b/app/assets/javascripts/angular/work_packages/view_models/relations-handler.js @@ -127,18 +127,30 @@ angular.module('openproject.viewModels') return ChildrenRelationsHandler; }]) -.factory('ParentRelationsHandler', ['ChildrenRelationsHandler', - function(ChildrenRelationsHandler) { - function ParentRelationsHandler(workPackage, parents) { - var handler = new ChildrenRelationsHandler(workPackage, parents, undefined); - - handler.type = "parent"; - handler.canAddRelation = function() { return false }; - handler.addRelation = undefined; - handler.isSingletonRelation = true; - - return handler; - } - - return ParentRelationsHandler; -}]); +.factory('ParentRelationsHandler', ['CommonRelationsHandler', 'WorkPackageService', 'ApiHelper', + function(CommonRelationsHandler, WorkPackageService, ApiHelper) { + function ParentRelationsHandler(workPackage, parents, relationsId) { + var handler = new CommonRelationsHandler(workPackage, parents, relationsId); + + handler.type = "parent"; + handler.addRelation = undefined; + handler.isSingletonRelation = true; + handler.relationsId = relationsId; + + handler.canAddRelation = function() { return !!this.workPackage.links.update; }; + handler.getRelatedWorkPackage = function(workPackage, relation) { return relation.fetch() }; + handler.addRelation = function(scope) { + var inputElement = angular.element('#relation_to_id-' + this.relationsId); + var parentId = inputElement.val(); + WorkPackageService.updateWorkPackage(this.workPackage, {parentId: parentId}).then(function(workPackage) { + inputElement.val(''); + scope.$emit('workPackageRefreshRequired', ''); + }, function(error) { + ApiHelper.handleError(scope, error); + }); + }; + + return handler; + } + return ParentRelationsHandler; +}]) diff --git a/app/assets/javascripts/angular/work_packages/work-package-context-menu.js b/app/assets/javascripts/angular/work_packages/work-package-context-menu.js index 5faa0d0bea..817f865caa 100644 --- a/app/assets/javascripts/angular/work_packages/work-package-context-menu.js +++ b/app/assets/javascripts/angular/work_packages/work-package-context-menu.js @@ -55,6 +55,10 @@ angular.module('openproject.workPackages') $scope.hideResourceActions = true; $scope.$watch('row', function() { + if (!$scope.row.checked) { + WorkPackagesTableService.setCheckedStateForAllRows($scope.rows, false); + } + $scope.row.checked = true; $scope.permittedActions = WorkPackageContextMenuHelper.getPermittedActions(getSelectedWorkPackages()); }); diff --git a/app/assets/javascripts/jstoolbar.js b/app/assets/javascripts/jstoolbar.js index 197da970d1..aca90d27fa 100644 --- a/app/assets/javascripts/jstoolbar.js +++ b/app/assets/javascripts/jstoolbar.js @@ -27,4 +27,5 @@ //++ //= require jstoolbar/jstoolbar +//= require jstoolbar/translations //= require jstoolbar/textile diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bg.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-bg.js deleted file mode 100644 index 2d68498f99..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bg.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Strong'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Underline'; -jsToolBar.strings['Deleted'] = 'Deleted'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Unordered list'; -jsToolBar.strings['Ordered list'] = 'Ordered list'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Link to a Wiki page'; -jsToolBar.strings['Image'] = 'Image'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bs.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-bs.js deleted file mode 100644 index c9b6773462..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bs.js +++ /dev/null @@ -1,14 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Strong'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Underline'; -jsToolBar.strings['Deleted'] = 'Deleted'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Unordered list'; -jsToolBar.strings['Ordered list'] = 'Ordered list'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Link na Wiki stranicu'; -jsToolBar.strings['Image'] = 'Slika'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ca.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ca.js deleted file mode 100644 index 3d652a4a57..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ca.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Negreta'; -jsToolBar.strings['Italic'] = 'Cursiva'; -jsToolBar.strings['Underline'] = 'Subratllat'; -jsToolBar.strings['Deleted'] = 'Barrat'; -jsToolBar.strings['Code'] = 'Codi en línia'; -jsToolBar.strings['Heading 1'] = 'Encapçalament 1'; -jsToolBar.strings['Heading 2'] = 'Encapçalament 2'; -jsToolBar.strings['Heading 3'] = 'Encapçalament 3'; -jsToolBar.strings['Unordered list'] = 'Llista sense ordre'; -jsToolBar.strings['Ordered list'] = 'Llista ordenada'; -jsToolBar.strings['Quote'] = 'Cometes'; -jsToolBar.strings['Unquote'] = 'Sense cometes'; -jsToolBar.strings['Preformatted text'] = 'Text formatat'; -jsToolBar.strings['Wiki link'] = 'Enllaça a una pàgina Wiki'; -jsToolBar.strings['Image'] = 'Imatge'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-cs.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-cs.js deleted file mode 100644 index f2c0dbff50..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-cs.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Tučné'; -jsToolBar.strings['Italic'] = 'Kurzíva'; -jsToolBar.strings['Underline'] = 'Podtržené'; -jsToolBar.strings['Deleted'] = 'Přeškrtnuté '; -jsToolBar.strings['Code'] = 'Zobrazení kódu'; -jsToolBar.strings['Heading 1'] = 'Záhlaví 1'; -jsToolBar.strings['Heading 2'] = 'Záhlaví 2'; -jsToolBar.strings['Heading 3'] = 'Záhlaví 3'; -jsToolBar.strings['Unordered list'] = 'Seznam'; -jsToolBar.strings['Ordered list'] = 'Uspořádaný seznam'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Předformátovaný text'; -jsToolBar.strings['Wiki link'] = 'Vložit odkaz na Wiki stránku'; -jsToolBar.strings['Image'] = 'Vložit obrázek'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-da.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-da.js deleted file mode 100644 index 53b2c36f0f..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-da.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Fed'; -jsToolBar.strings['Italic'] = 'Kursiv'; -jsToolBar.strings['Underline'] = 'Understreget'; -jsToolBar.strings['Deleted'] = 'Slettet'; -jsToolBar.strings['Code'] = 'Inline-kode'; -jsToolBar.strings['Heading 1'] = 'Overskrift 1'; -jsToolBar.strings['Heading 2'] = 'Overskrift 2'; -jsToolBar.strings['Heading 3'] = 'Overskrift 3'; -jsToolBar.strings['Unordered list'] = 'Unummereret liste'; -jsToolBar.strings['Ordered list'] = 'Nummereret liste'; -jsToolBar.strings['Quote'] = 'Citér'; -jsToolBar.strings['Unquote'] = 'Fjern citér'; -jsToolBar.strings['Preformatted text'] = 'Præformateret tekst'; -jsToolBar.strings['Wiki link'] = 'Link til en wiki-side'; -jsToolBar.strings['Image'] = 'Billede'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-de.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-de.js deleted file mode 100644 index ce686860f4..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-de.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Fett'; -jsToolBar.strings['Italic'] = 'Kursiv'; -jsToolBar.strings['Underline'] = 'Unterstrichen'; -jsToolBar.strings['Deleted'] = 'Durchgestrichen'; -jsToolBar.strings['Code'] = 'Quelltext'; -jsToolBar.strings['Heading 1'] = 'Überschrift 1. Ordnung'; -jsToolBar.strings['Heading 2'] = 'Überschrift 2. Ordnung'; -jsToolBar.strings['Heading 3'] = 'Überschrift 3. Ordnung'; -jsToolBar.strings['Unordered list'] = 'Aufzählungsliste'; -jsToolBar.strings['Ordered list'] = 'Nummerierte Liste'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Präformatierter Text'; -jsToolBar.strings['Wiki link'] = 'Verweis (Link) zu einer Wiki-Seite'; -jsToolBar.strings['Image'] = 'Grafik'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en-gb.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-en-gb.js deleted file mode 100644 index 2d68498f99..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en-gb.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Strong'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Underline'; -jsToolBar.strings['Deleted'] = 'Deleted'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Unordered list'; -jsToolBar.strings['Ordered list'] = 'Ordered list'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Link to a Wiki page'; -jsToolBar.strings['Image'] = 'Image'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-en.js deleted file mode 100644 index 2d68498f99..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Strong'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Underline'; -jsToolBar.strings['Deleted'] = 'Deleted'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Unordered list'; -jsToolBar.strings['Ordered list'] = 'Ordered list'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Link to a Wiki page'; -jsToolBar.strings['Image'] = 'Image'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-es.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-es.js deleted file mode 100644 index 878489fbf2..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-es.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Negrita'; -jsToolBar.strings['Italic'] = 'Itálica'; -jsToolBar.strings['Underline'] = 'Subrayado'; -jsToolBar.strings['Deleted'] = 'Tachado'; -jsToolBar.strings['Code'] = 'Código fuente'; -jsToolBar.strings['Heading 1'] = 'Encabezado 1'; -jsToolBar.strings['Heading 2'] = 'Encabezado 2'; -jsToolBar.strings['Heading 3'] = 'Encabezado 3'; -jsToolBar.strings['Unordered list'] = 'Lista sin ordenar'; -jsToolBar.strings['Ordered list'] = 'Lista ordenada'; -jsToolBar.strings['Quote'] = 'Citar'; -jsToolBar.strings['Unquote'] = 'Quitar cita'; -jsToolBar.strings['Preformatted text'] = 'Texto con formato'; -jsToolBar.strings['Wiki link'] = 'Enlace a página Wiki'; -jsToolBar.strings['Image'] = 'Imagen'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-eu.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-eu.js deleted file mode 100644 index 3067fc8aba..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-eu.js +++ /dev/null @@ -1,20 +0,0 @@ -// jsToolBar EU language -// Author: Ales Zabala Alava (Shagi), -// 2010-01-25 -// Distributed under the same terms as the jsToolBar itself. -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Lodia'; -jsToolBar.strings['Italic'] = 'Etzana'; -jsToolBar.strings['Underline'] = 'Azpimarra'; -jsToolBar.strings['Deleted'] = 'Ezabatuta'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = '1 Goiburua'; -jsToolBar.strings['Heading 2'] = '2 Goiburua'; -jsToolBar.strings['Heading 3'] = '3 Goiburua'; -jsToolBar.strings['Unordered list'] = 'Ordenatu gabeko zerrenda'; -jsToolBar.strings['Ordered list'] = 'Ordenatutako zerrenda'; -jsToolBar.strings['Quote'] = 'Aipamena'; -jsToolBar.strings['Unquote'] = 'Aipamena kendu'; -jsToolBar.strings['Preformatted text'] = 'Aurrez formateatutako testua'; -jsToolBar.strings['Wiki link'] = 'Wiki orri baterako esteka'; -jsToolBar.strings['Image'] = 'Irudia'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fa.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-fa.js deleted file mode 100644 index 61236bf6f6..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fa.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'پررنگ'; -jsToolBar.strings['Italic'] = 'کج'; -jsToolBar.strings['Underline'] = 'زیرخط'; -jsToolBar.strings['Deleted'] = 'برداشته شده'; -jsToolBar.strings['Code'] = 'کد درون خطی'; -jsToolBar.strings['Heading 1'] = 'سربرگ ۱'; -jsToolBar.strings['Heading 2'] = 'سربرگ ۲'; -jsToolBar.strings['Heading 3'] = 'سربرگ ۳'; -jsToolBar.strings['Unordered list'] = 'فهرست بدون شماره'; -jsToolBar.strings['Ordered list'] = 'فهرست با شماره'; -jsToolBar.strings['Quote'] = 'تو بردن'; -jsToolBar.strings['Unquote'] = 'بیرون آوردن'; -jsToolBar.strings['Preformatted text'] = 'نوشته قالب بندی شده'; -jsToolBar.strings['Wiki link'] = 'پیوند به برگ ویکی'; -jsToolBar.strings['Image'] = 'عکس'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fi.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-fi.js deleted file mode 100644 index c2229b281a..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fi.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Lihavoitu'; -jsToolBar.strings['Italic'] = 'Kursivoitu'; -jsToolBar.strings['Underline'] = 'Alleviivattu'; -jsToolBar.strings['Deleted'] = 'Yliviivattu'; -jsToolBar.strings['Code'] = 'Koodi näkymä'; -jsToolBar.strings['Heading 1'] = 'Otsikko 1'; -jsToolBar.strings['Heading 2'] = 'Otsikko 2'; -jsToolBar.strings['Heading 3'] = 'Otsikko 3'; -jsToolBar.strings['Unordered list'] = 'Järjestämätön lista'; -jsToolBar.strings['Ordered list'] = 'Järjestetty lista'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Ennaltamuotoiltu teksti'; -jsToolBar.strings['Wiki link'] = 'Linkki Wiki sivulle'; -jsToolBar.strings['Image'] = 'Kuva'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-fr.js deleted file mode 100644 index c52a783bca..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fr.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Gras'; -jsToolBar.strings['Italic'] = 'Italique'; -jsToolBar.strings['Underline'] = 'Souligné'; -jsToolBar.strings['Deleted'] = 'Rayé'; -jsToolBar.strings['Code'] = 'Code en ligne'; -jsToolBar.strings['Heading 1'] = 'Titre niveau 1'; -jsToolBar.strings['Heading 2'] = 'Titre niveau 2'; -jsToolBar.strings['Heading 3'] = 'Titre niveau 3'; -jsToolBar.strings['Unordered list'] = 'Liste à puces'; -jsToolBar.strings['Ordered list'] = 'Liste numérotée'; -jsToolBar.strings['Quote'] = 'Citer'; -jsToolBar.strings['Unquote'] = 'Supprimer citation'; -jsToolBar.strings['Preformatted text'] = 'Texte préformaté'; -jsToolBar.strings['Wiki link'] = 'Lien vers une page Wiki'; -jsToolBar.strings['Image'] = 'Image'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-gl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-gl.js deleted file mode 100644 index bd1462aebe..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-gl.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Negriña'; -jsToolBar.strings['Italic'] = 'Itálica'; -jsToolBar.strings['Underline'] = 'Suliñado'; -jsToolBar.strings['Deleted'] = 'Tachado'; -jsToolBar.strings['Code'] = 'Código fonte'; -jsToolBar.strings['Heading 1'] = 'Encabezado 1'; -jsToolBar.strings['Heading 2'] = 'Encabezado 2'; -jsToolBar.strings['Heading 3'] = 'Encabezado 3'; -jsToolBar.strings['Unordered list'] = 'Lista sen ordenar'; -jsToolBar.strings['Ordered list'] = 'Lista ordenada'; -jsToolBar.strings['Quote'] = 'Citar'; -jsToolBar.strings['Unquote'] = 'Quitar cita'; -jsToolBar.strings['Preformatted text'] = 'Texto con formato'; -jsToolBar.strings['Wiki link'] = 'Enlace a páxina Wiki'; -jsToolBar.strings['Image'] = 'Imaxe'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-he.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-he.js deleted file mode 100644 index 2d68498f99..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-he.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Strong'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Underline'; -jsToolBar.strings['Deleted'] = 'Deleted'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Unordered list'; -jsToolBar.strings['Ordered list'] = 'Ordered list'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Link to a Wiki page'; -jsToolBar.strings['Image'] = 'Image'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-hr.js deleted file mode 100644 index 578ae7c095..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hr.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Podebljano'; -jsToolBar.strings['Italic'] = 'Kurziv'; -jsToolBar.strings['Underline'] = 'Podcrtano'; -jsToolBar.strings['Deleted'] = 'Obrisano'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Naslov 1'; -jsToolBar.strings['Heading 2'] = 'Naslov 2'; -jsToolBar.strings['Heading 3'] = 'Naslov 3'; -jsToolBar.strings['Unordered list'] = 'Graficke oznake'; -jsToolBar.strings['Ordered list'] = 'Numeriranje'; -jsToolBar.strings['Quote'] = 'Citat'; -jsToolBar.strings['Unquote'] = 'Ukloni citat'; -jsToolBar.strings['Preformatted text'] = 'Izveden tekst'; -jsToolBar.strings['Wiki link'] = 'Link na Wiki stranicu'; -jsToolBar.strings['Image'] = 'Slika'; \ No newline at end of file diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hu.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-hu.js deleted file mode 100644 index c31ba00c05..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hu.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Félkövér'; -jsToolBar.strings['Italic'] = 'Dőlt'; -jsToolBar.strings['Underline'] = 'Aláhúzott'; -jsToolBar.strings['Deleted'] = 'Törölt'; -jsToolBar.strings['Code'] = 'Kód sorok'; -jsToolBar.strings['Heading 1'] = 'Fejléc 1'; -jsToolBar.strings['Heading 2'] = 'Fejléc 2'; -jsToolBar.strings['Heading 3'] = 'Fejléc 3'; -jsToolBar.strings['Unordered list'] = 'Felsorolás'; -jsToolBar.strings['Ordered list'] = 'Számozott lista'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Előreformázott szöveg'; -jsToolBar.strings['Wiki link'] = 'Link egy Wiki oldalra'; -jsToolBar.strings['Image'] = 'Kép'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-id.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-id.js deleted file mode 100644 index cca978ad95..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-id.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Tebal'; -jsToolBar.strings['Italic'] = 'Miring'; -jsToolBar.strings['Underline'] = 'Garis bawah'; -jsToolBar.strings['Deleted'] = 'Dihapus'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Judul 1'; -jsToolBar.strings['Heading 2'] = 'Judul 2'; -jsToolBar.strings['Heading 3'] = 'Judul 3'; -jsToolBar.strings['Unordered list'] = 'Daftar tak terurut'; -jsToolBar.strings['Ordered list'] = 'Daftar terurut'; -jsToolBar.strings['Quote'] = 'Kutipan'; -jsToolBar.strings['Unquote'] = 'Hapus kutipan'; -jsToolBar.strings['Preformatted text'] = 'Teks terformat'; -jsToolBar.strings['Wiki link'] = 'Tautkan ke halaman wiki'; -jsToolBar.strings['Image'] = 'Gambar'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-it.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-it.js deleted file mode 100644 index 99749b40c2..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-it.js +++ /dev/null @@ -1,19 +0,0 @@ -// Italian translation -// by Diego Pierotto (ita.translations@tiscali.it) - -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Grassetto'; -jsToolBar.strings['Italic'] = 'Corsivo'; -jsToolBar.strings['Underline'] = 'Sottolineato'; -jsToolBar.strings['Deleted'] = 'Barrato'; -jsToolBar.strings['Code'] = 'Codice sorgente'; -jsToolBar.strings['Heading 1'] = 'Titolo 1'; -jsToolBar.strings['Heading 2'] = 'Titolo 2'; -jsToolBar.strings['Heading 3'] = 'Titolo 3'; -jsToolBar.strings['Unordered list'] = 'Elenco puntato'; -jsToolBar.strings['Ordered list'] = 'Elenco numerato'; -jsToolBar.strings['Quote'] = 'Aumenta rientro'; -jsToolBar.strings['Unquote'] = 'Riduci rientro'; -jsToolBar.strings['Preformatted text'] = 'Testo preformattato'; -jsToolBar.strings['Wiki link'] = 'Collegamento a pagina Wiki'; -jsToolBar.strings['Image'] = 'Immagine'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ja.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ja.js deleted file mode 100644 index 0073271e1f..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ja.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = '強調'; -jsToolBar.strings['Italic'] = '斜体'; -jsToolBar.strings['Underline'] = '下線'; -jsToolBar.strings['Deleted'] = '取り消し線'; -jsToolBar.strings['Code'] = 'コード'; -jsToolBar.strings['Heading 1'] = '見出し 1'; -jsToolBar.strings['Heading 2'] = '見出し 2'; -jsToolBar.strings['Heading 3'] = '見出し 3'; -jsToolBar.strings['Unordered list'] = '順不同リスト'; -jsToolBar.strings['Ordered list'] = '番号つきリスト'; -jsToolBar.strings['Quote'] = '引用'; -jsToolBar.strings['Unquote'] = '引用解除'; -jsToolBar.strings['Preformatted text'] = '整形済みテキスト'; -jsToolBar.strings['Wiki link'] = 'Wikiページへのリンク'; -jsToolBar.strings['Image'] = '画像'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ko.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ko.js deleted file mode 100644 index 1c437ef763..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ko.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = '굵게'; -jsToolBar.strings['Italic'] = '기울임'; -jsToolBar.strings['Underline'] = '밑줄'; -jsToolBar.strings['Deleted'] = '취소선'; -jsToolBar.strings['Code'] = '코드'; -jsToolBar.strings['Heading 1'] = '제목 1'; -jsToolBar.strings['Heading 2'] = '제목 2'; -jsToolBar.strings['Heading 3'] = '제목 3'; -jsToolBar.strings['Unordered list'] = '글머리 기호'; -jsToolBar.strings['Ordered list'] = '번호 매기기'; -jsToolBar.strings['Quote'] = '인용'; -jsToolBar.strings['Unquote'] = '인용 취소'; -jsToolBar.strings['Preformatted text'] = '있는 그대로 표현 (Preformatted text)'; -jsToolBar.strings['Wiki link'] = 'Wiki 페이지에 연결'; -jsToolBar.strings['Image'] = '그림'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lt.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-lt.js deleted file mode 100644 index 8af364c8d5..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lt.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Pastorinti'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Pabraukti'; -jsToolBar.strings['Deleted'] = 'Užbraukti'; -jsToolBar.strings['Code'] = 'Kodas'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Nenumeruotas sąrašas'; -jsToolBar.strings['Ordered list'] = 'Numeruotas sąrašas'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Preformatuotas tekstas'; -jsToolBar.strings['Wiki link'] = 'Nuoroda į Wiki puslapį'; -jsToolBar.strings['Image'] = 'Paveikslas'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lv.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-lv.js deleted file mode 100644 index b82150047e..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lv.js +++ /dev/null @@ -1,17 +0,0 @@ -// translated by Dzintars Bergs (dzintars.bergs@gmail.com) -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Treknraksts'; -jsToolBar.strings['Italic'] = 'Slīpraksts'; -jsToolBar.strings['Underline'] = 'Pasvītrojums'; -jsToolBar.strings['Deleted'] = 'Dzēsts'; -jsToolBar.strings['Code'] = 'Iekļauts kods'; -jsToolBar.strings['Heading 1'] = 'Virsraksts 1'; -jsToolBar.strings['Heading 2'] = 'Virsraksts 2'; -jsToolBar.strings['Heading 3'] = 'Virsraksts 3'; -jsToolBar.strings['Unordered list'] = 'Nesakārtots saraksts'; -jsToolBar.strings['Ordered list'] = 'Sakārtots saraksts'; -jsToolBar.strings['Quote'] = 'Citēt'; -jsToolBar.strings['Unquote'] = 'Noņemt citātu'; -jsToolBar.strings['Preformatted text'] = 'Iepriekš formatēts teksts'; -jsToolBar.strings['Wiki link'] = 'Saite uz Wiki lapu'; -jsToolBar.strings['Image'] = 'Attēls'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mk.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-mk.js deleted file mode 100644 index 30c68ec6cc..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mk.js +++ /dev/null @@ -1,17 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Задебелен'; -jsToolBar.strings['Italic'] = 'Закосен'; -jsToolBar.strings['Underline'] = 'Подвлечен'; -jsToolBar.strings['Deleted'] = 'Прецртан'; -jsToolBar.strings['Code'] = 'Код'; -jsToolBar.strings['Heading 1'] = 'Заглавје 1'; -jsToolBar.strings['Heading 2'] = 'Заглавје 2'; -jsToolBar.strings['Heading 3'] = 'Заглавје 3'; -jsToolBar.strings['Unordered list'] = 'Неподредена листа'; -jsToolBar.strings['Ordered list'] = 'Подредена листа'; -jsToolBar.strings['Quote'] = 'Цитат'; -jsToolBar.strings['Unquote'] = 'Отстрани цитат'; -jsToolBar.strings['Preformatted text'] = 'Форматиран текст'; -jsToolBar.strings['Wiki link'] = 'Врска до вики страна'; -jsToolBar.strings['Image'] = 'Слика'; - diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mn.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-mn.js deleted file mode 100644 index ef1da465bb..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mn.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Бүдүүн'; -jsToolBar.strings['Italic'] = 'Налуу'; -jsToolBar.strings['Underline'] = 'Доогуур зураас'; -jsToolBar.strings['Deleted'] = 'Устгагдсан'; -jsToolBar.strings['Code'] = 'Програмын код'; -jsToolBar.strings['Heading 1'] = 'Гарчиг 1'; -jsToolBar.strings['Heading 2'] = 'Гарчиг 2'; -jsToolBar.strings['Heading 3'] = 'Гарчиг 3'; -jsToolBar.strings['Unordered list'] = 'Эрэмбэгүй жагсаалт'; -jsToolBar.strings['Ordered list'] = 'Эрэмбэтэй жагсаалт'; -jsToolBar.strings['Quote'] = 'Ишлэл'; -jsToolBar.strings['Unquote'] = 'Ишлэлийг устгах'; -jsToolBar.strings['Preformatted text'] = 'Өмнө нь хэлбэржсэн текст'; -jsToolBar.strings['Wiki link'] = 'Вики хуудас руу холбох'; -jsToolBar.strings['Image'] = 'Зураг'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-nl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-nl.js deleted file mode 100644 index 0c54163f0f..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-nl.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Extra nadruk'; -jsToolBar.strings['Italic'] = 'Cursief'; -jsToolBar.strings['Underline'] = 'Onderstreept'; -jsToolBar.strings['Deleted'] = 'Verwijderd'; -jsToolBar.strings['Code'] = 'Computercode'; -jsToolBar.strings['Heading 1'] = 'Kop 1'; -jsToolBar.strings['Heading 2'] = 'Kop 2'; -jsToolBar.strings['Heading 3'] = 'Kop 3'; -jsToolBar.strings['Unordered list'] = 'Ongeordende lijst'; -jsToolBar.strings['Ordered list'] = 'Geordende lijst'; -jsToolBar.strings['Quote'] = 'Citaat'; -jsToolBar.strings['Unquote'] = 'Verwijder citaat'; -jsToolBar.strings['Preformatted text'] = 'Voor-geformateerde tekst'; -jsToolBar.strings['Wiki link'] = 'Link naar een Wiki pagina'; -jsToolBar.strings['Image'] = 'Afbeelding'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-no.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-no.js deleted file mode 100644 index 7995973439..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-no.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Fet'; -jsToolBar.strings['Italic'] = 'Kursiv'; -jsToolBar.strings['Underline'] = 'Understreking'; -jsToolBar.strings['Deleted'] = 'Slettet'; -jsToolBar.strings['Code'] = 'Kode'; -jsToolBar.strings['Heading 1'] = 'Overskrift 1'; -jsToolBar.strings['Heading 2'] = 'Overskrift 2'; -jsToolBar.strings['Heading 3'] = 'Overskrift 3'; -jsToolBar.strings['Unordered list'] = 'Punktliste'; -jsToolBar.strings['Ordered list'] = 'Nummerert liste'; -jsToolBar.strings['Quote'] = 'Sitat'; -jsToolBar.strings['Unquote'] = 'Avslutt sitat'; -jsToolBar.strings['Preformatted text'] = 'Preformatert tekst'; -jsToolBar.strings['Wiki link'] = 'Lenke til Wiki-side'; -jsToolBar.strings['Image'] = 'Bilde'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-pl.js deleted file mode 100644 index 0e7a38c902..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pl.js +++ /dev/null @@ -1,17 +0,0 @@ -// Keep this line in order to avoid problems with Windows Notepad UTF-8 EF-BB-BF idea... -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Pogrubienie'; -jsToolBar.strings['Italic'] = 'Kursywa'; -jsToolBar.strings['Underline'] = 'Podkreślenie'; -jsToolBar.strings['Deleted'] = 'Usunięte'; -jsToolBar.strings['Code'] = 'Wstawka kodu'; -jsToolBar.strings['Heading 1'] = 'Nagłowek 1'; -jsToolBar.strings['Heading 2'] = 'Nagłówek 2'; -jsToolBar.strings['Heading 3'] = 'Nagłówek 3'; -jsToolBar.strings['Unordered list'] = 'Nieposortowana lista'; -jsToolBar.strings['Ordered list'] = 'Posortowana lista'; -jsToolBar.strings['Quote'] = 'Cytat'; -jsToolBar.strings['Unquote'] = 'Usuń cytat'; -jsToolBar.strings['Preformatted text'] = 'Sformatowany tekst'; -jsToolBar.strings['Wiki link'] = 'Odnośnik do strony Wiki'; -jsToolBar.strings['Image'] = 'Obraz'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt-br.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt-br.js deleted file mode 100644 index 5035524ab4..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt-br.js +++ /dev/null @@ -1,18 +0,0 @@ -// Translated by: Alexandre da Silva - -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Negrito'; -jsToolBar.strings['Italic'] = 'Itálico'; -jsToolBar.strings['Underline'] = 'Sublinhado'; -jsToolBar.strings['Deleted'] = 'Excluído'; -jsToolBar.strings['Code'] = 'Código Inline'; -jsToolBar.strings['Heading 1'] = 'Cabeçalho 1'; -jsToolBar.strings['Heading 2'] = 'Cabeçalho 2'; -jsToolBar.strings['Heading 3'] = 'Cabeçalho 3'; -jsToolBar.strings['Unordered list'] = 'Lista não ordenada'; -jsToolBar.strings['Ordered list'] = 'Lista ordenada'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Texto pré-formatado'; -jsToolBar.strings['Wiki link'] = 'Link para uma página Wiki'; -jsToolBar.strings['Image'] = 'Imagem'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt.js deleted file mode 100644 index 137d7952a4..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt.js +++ /dev/null @@ -1,17 +0,0 @@ -// Translated by: Pedro Araújo -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Negrito'; -jsToolBar.strings['Italic'] = 'Itálico'; -jsToolBar.strings['Underline'] = 'Sublinhado'; -jsToolBar.strings['Deleted'] = 'Apagado'; -jsToolBar.strings['Code'] = 'Código Inline'; -jsToolBar.strings['Heading 1'] = 'Cabeçalho 1'; -jsToolBar.strings['Heading 2'] = 'Cabeçalho 2'; -jsToolBar.strings['Heading 3'] = 'Cabeçalho 3'; -jsToolBar.strings['Unordered list'] = 'Lista não ordenada'; -jsToolBar.strings['Ordered list'] = 'Lista ordenada'; -jsToolBar.strings['Quote'] = 'Citação'; -jsToolBar.strings['Unquote'] = 'Remover citação'; -jsToolBar.strings['Preformatted text'] = 'Texto pré-formatado'; -jsToolBar.strings['Wiki link'] = 'Link para uma página da Wiki'; -jsToolBar.strings['Image'] = 'Imagem'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ro.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ro.js deleted file mode 100644 index fdaec4a207..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ro.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Bold'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Subliniat'; -jsToolBar.strings['Deleted'] = 'Șters'; -jsToolBar.strings['Code'] = 'Fragment de cod'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Listă pe puncte'; -jsToolBar.strings['Ordered list'] = 'Listă ordonată'; -jsToolBar.strings['Quote'] = 'Citează'; -jsToolBar.strings['Unquote'] = 'Fără citat'; -jsToolBar.strings['Preformatted text'] = 'Text preformatat'; -jsToolBar.strings['Wiki link'] = 'Trimitere către o pagină wiki'; -jsToolBar.strings['Image'] = 'Imagine'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ru.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ru.js deleted file mode 100644 index f4c810138a..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ru.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Жирный'; -jsToolBar.strings['Italic'] = 'Курсив'; -jsToolBar.strings['Underline'] = 'Подчеркнутый'; -jsToolBar.strings['Deleted'] = 'Зачеркнутый'; -jsToolBar.strings['Code'] = 'Вставка кода'; -jsToolBar.strings['Heading 1'] = 'Заголовок 1'; -jsToolBar.strings['Heading 2'] = 'Заголовок 2'; -jsToolBar.strings['Heading 3'] = 'Заголовок 3'; -jsToolBar.strings['Unordered list'] = 'Маркированный список'; -jsToolBar.strings['Ordered list'] = 'Нумерованный список'; -jsToolBar.strings['Quote'] = 'Цитата'; -jsToolBar.strings['Unquote'] = 'Удалить цитату'; -jsToolBar.strings['Preformatted text'] = 'Заранее форматированный текст'; -jsToolBar.strings['Wiki link'] = 'Ссылка на страницу в Wiki'; -jsToolBar.strings['Image'] = 'Вставка изображения'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sk.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sk.js deleted file mode 100644 index 0d47cd5162..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sk.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Tučné'; -jsToolBar.strings['Italic'] = 'Kurzíva'; -jsToolBar.strings['Underline'] = 'Podčiarknuté'; -jsToolBar.strings['Deleted'] = 'Preškrtnuté'; -jsToolBar.strings['Code'] = 'Zobrazenie kódu'; -jsToolBar.strings['Heading 1'] = 'Záhlavie 1'; -jsToolBar.strings['Heading 2'] = 'Záhlavie 2'; -jsToolBar.strings['Heading 3'] = 'Záhlavie 3'; -jsToolBar.strings['Unordered list'] = 'Zoznam'; -jsToolBar.strings['Ordered list'] = 'Zoradený zoznam'; -jsToolBar.strings['Quote'] = 'Citácia'; -jsToolBar.strings['Unquote'] = 'Odstránenie citácie'; -jsToolBar.strings['Preformatted text'] = 'Predformátovaný text'; -jsToolBar.strings['Wiki link'] = 'Link na Wiki stránku'; -jsToolBar.strings['Image'] = 'Obrázok'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sl.js deleted file mode 100644 index 70949957e4..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sl.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Krepko'; -jsToolBar.strings['Italic'] = 'Poševno'; -jsToolBar.strings['Underline'] = 'Podčrtano'; -jsToolBar.strings['Deleted'] = 'Izbrisano'; -jsToolBar.strings['Code'] = 'Koda med vrsticami'; -jsToolBar.strings['Heading 1'] = 'Naslov 1'; -jsToolBar.strings['Heading 2'] = 'Naslov 2'; -jsToolBar.strings['Heading 3'] = 'Naslov 3'; -jsToolBar.strings['Unordered list'] = 'Neurejen seznam'; -jsToolBar.strings['Ordered list'] = 'Urejen seznam'; -jsToolBar.strings['Quote'] = 'Citat'; -jsToolBar.strings['Unquote'] = 'Odstrani citat'; -jsToolBar.strings['Preformatted text'] = 'Predoblikovano besedilo'; -jsToolBar.strings['Wiki link'] = 'Povezava na Wiki stran'; -jsToolBar.strings['Image'] = 'Slika'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr-yu.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr-yu.js deleted file mode 100644 index 0e231e0291..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr-yu.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Podebljano'; -jsToolBar.strings['Italic'] = 'Kurziv'; -jsToolBar.strings['Underline'] = 'Podvučeno'; -jsToolBar.strings['Deleted'] = 'Obrisano'; -jsToolBar.strings['Code'] = 'Ugrađeni kôd'; -jsToolBar.strings['Heading 1'] = 'Naslov 1'; -jsToolBar.strings['Heading 2'] = 'Naslov 2'; -jsToolBar.strings['Heading 3'] = 'Naslov 3'; -jsToolBar.strings['Unordered list'] = 'Lista nabrajanja'; -jsToolBar.strings['Ordered list'] = 'Uređena lista'; -jsToolBar.strings['Quote'] = 'Pod navodnicima'; -jsToolBar.strings['Unquote'] = 'Ukloni navodnike'; -jsToolBar.strings['Preformatted text'] = 'Prethodno formatiran tekst'; -jsToolBar.strings['Wiki link'] = 'Veza prema Wiki strani'; -jsToolBar.strings['Image'] = 'Slika'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr.js deleted file mode 100644 index 75a768ad02..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Подебљано'; -jsToolBar.strings['Italic'] = 'Курзив'; -jsToolBar.strings['Underline'] = 'Подвучено'; -jsToolBar.strings['Deleted'] = 'Обрисано'; -jsToolBar.strings['Code'] = 'Уграђени кôд'; -jsToolBar.strings['Heading 1'] = 'Наслов 1'; -jsToolBar.strings['Heading 2'] = 'Наслов 2'; -jsToolBar.strings['Heading 3'] = 'Наслов 3'; -jsToolBar.strings['Unordered list'] = 'Листа набрајања'; -jsToolBar.strings['Ordered list'] = 'Уређена листа'; -jsToolBar.strings['Quote'] = 'Под наводницима'; -jsToolBar.strings['Unquote'] = 'Уклони наводнике'; -jsToolBar.strings['Preformatted text'] = 'Претходно форматиран текст'; -jsToolBar.strings['Wiki link'] = 'Веза према Wiki страни'; -jsToolBar.strings['Image'] = 'Слика'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sv.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sv.js deleted file mode 100644 index 08c0b69a32..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sv.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Fet'; -jsToolBar.strings['Italic'] = 'Kursiv'; -jsToolBar.strings['Underline'] = 'Understruken'; -jsToolBar.strings['Deleted'] = 'Genomstruken'; -jsToolBar.strings['Code'] = 'Kod'; -jsToolBar.strings['Heading 1'] = 'Rubrik 1'; -jsToolBar.strings['Heading 2'] = 'Rubrik 2'; -jsToolBar.strings['Heading 3'] = 'Rubrik 3'; -jsToolBar.strings['Unordered list'] = 'Osorterad lista'; -jsToolBar.strings['Ordered list'] = 'Sorterad lista'; -jsToolBar.strings['Quote'] = 'Citat'; -jsToolBar.strings['Unquote'] = 'Ta bort citat'; -jsToolBar.strings['Preformatted text'] = 'Förformaterad text'; -jsToolBar.strings['Wiki link'] = 'Länk till en wikisida'; -jsToolBar.strings['Image'] = 'Bild'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-th.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-th.js deleted file mode 100644 index d87164226c..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-th.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'หนา'; -jsToolBar.strings['Italic'] = 'เอียง'; -jsToolBar.strings['Underline'] = 'ขีดเส้นใต้'; -jsToolBar.strings['Deleted'] = 'ขีดฆ่า'; -jsToolBar.strings['Code'] = 'โค๊ดโปรแกรม'; -jsToolBar.strings['Heading 1'] = 'หัวข้อ 1'; -jsToolBar.strings['Heading 2'] = 'หัวข้อ 2'; -jsToolBar.strings['Heading 3'] = 'หัวข้อ 3'; -jsToolBar.strings['Unordered list'] = 'รายการ'; -jsToolBar.strings['Ordered list'] = 'ลำดับเลข'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'รูปแบบข้อความคงที่'; -jsToolBar.strings['Wiki link'] = 'เชื่อมโยงไปหน้า Wiki อื่น'; -jsToolBar.strings['Image'] = 'รูปภาพ'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-tr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-tr.js deleted file mode 100644 index 31705d7c0f..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-tr.js +++ /dev/null @@ -1,14 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Kalın'; -jsToolBar.strings['Italic'] = 'İtalik'; -jsToolBar.strings['Underline'] = 'Altı çizgili'; -jsToolBar.strings['Deleted'] = 'Silinmiş'; -jsToolBar.strings['Code'] = 'Satır içi kod'; -jsToolBar.strings['Heading 1'] = 'Başlık 1'; -jsToolBar.strings['Heading 2'] = 'Başlık 2'; -jsToolBar.strings['Heading 3'] = 'Başlık 3'; -jsToolBar.strings['Unordered list'] = 'Sırasız liste'; -jsToolBar.strings['Ordered list'] = 'Sıralı liste'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Wiki sayfasına bağlantı'; -jsToolBar.strings['Image'] = 'Resim'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-uk.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-uk.js deleted file mode 100644 index 2d68498f99..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-uk.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Strong'; -jsToolBar.strings['Italic'] = 'Italic'; -jsToolBar.strings['Underline'] = 'Underline'; -jsToolBar.strings['Deleted'] = 'Deleted'; -jsToolBar.strings['Code'] = 'Inline Code'; -jsToolBar.strings['Heading 1'] = 'Heading 1'; -jsToolBar.strings['Heading 2'] = 'Heading 2'; -jsToolBar.strings['Heading 3'] = 'Heading 3'; -jsToolBar.strings['Unordered list'] = 'Unordered list'; -jsToolBar.strings['Ordered list'] = 'Ordered list'; -jsToolBar.strings['Quote'] = 'Quote'; -jsToolBar.strings['Unquote'] = 'Remove Quote'; -jsToolBar.strings['Preformatted text'] = 'Preformatted text'; -jsToolBar.strings['Wiki link'] = 'Link to a Wiki page'; -jsToolBar.strings['Image'] = 'Image'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-vi.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-vi.js deleted file mode 100644 index f598bfe0af..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-vi.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = 'Đậm'; -jsToolBar.strings['Italic'] = 'Nghiêng'; -jsToolBar.strings['Underline'] = 'Gạch chân'; -jsToolBar.strings['Deleted'] = 'Xóa'; -jsToolBar.strings['Code'] = 'Mã chung dòng'; -jsToolBar.strings['Heading 1'] = 'Tiêu đề 1'; -jsToolBar.strings['Heading 2'] = 'Tiêu đề 2'; -jsToolBar.strings['Heading 3'] = 'Tiêu đề 3'; -jsToolBar.strings['Unordered list'] = 'Danh sách không thứ tự'; -jsToolBar.strings['Ordered list'] = 'Danh sách có thứ tự'; -jsToolBar.strings['Quote'] = 'Trích dẫn'; -jsToolBar.strings['Unquote'] = 'Bỏ trích dẫn'; -jsToolBar.strings['Preformatted text'] = 'Mã nguồn'; -jsToolBar.strings['Wiki link'] = 'Liên kết đến trang wiki'; -jsToolBar.strings['Image'] = 'Ảnh'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh-tw.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh-tw.js deleted file mode 100644 index 86599c5a27..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh-tw.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = '粗體'; -jsToolBar.strings['Italic'] = '斜體'; -jsToolBar.strings['Underline'] = '底線'; -jsToolBar.strings['Deleted'] = '刪除線'; -jsToolBar.strings['Code'] = '程式碼'; -jsToolBar.strings['Heading 1'] = '標題 1'; -jsToolBar.strings['Heading 2'] = '標題 2'; -jsToolBar.strings['Heading 3'] = '標題 3'; -jsToolBar.strings['Unordered list'] = '項目清單'; -jsToolBar.strings['Ordered list'] = '編號清單'; -jsToolBar.strings['Quote'] = '引文'; -jsToolBar.strings['Unquote'] = '取消引文'; -jsToolBar.strings['Preformatted text'] = '已格式文字'; -jsToolBar.strings['Wiki link'] = '連結至 Wiki 頁面'; -jsToolBar.strings['Image'] = '圖片'; diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh.js deleted file mode 100644 index a9b6ba2301..0000000000 --- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh.js +++ /dev/null @@ -1,16 +0,0 @@ -jsToolBar.strings = {}; -jsToolBar.strings['Strong'] = '粗体'; -jsToolBar.strings['Italic'] = '斜体'; -jsToolBar.strings['Underline'] = '下划线'; -jsToolBar.strings['Deleted'] = '删除线'; -jsToolBar.strings['Code'] = '程序代码'; -jsToolBar.strings['Heading 1'] = '标题 1'; -jsToolBar.strings['Heading 2'] = '标题 2'; -jsToolBar.strings['Heading 3'] = '标题 3'; -jsToolBar.strings['Unordered list'] = '无序列表'; -jsToolBar.strings['Ordered list'] = '排序列表'; -jsToolBar.strings['Quote'] = '引用'; -jsToolBar.strings['Unquote'] = '删除引用'; -jsToolBar.strings['Preformatted text'] = '格式化文本'; -jsToolBar.strings['Wiki link'] = '连接到 Wiki 页面'; -jsToolBar.strings['Image'] = '图片'; diff --git a/app/assets/javascripts/jstoolbar/translations.js b/app/assets/javascripts/jstoolbar/translations.js new file mode 100644 index 0000000000..1874dc71ef --- /dev/null +++ b/app/assets/javascripts/jstoolbar/translations.js @@ -0,0 +1,20 @@ +jQuery(function() { + jsToolBar.strings = { + 'Strong': I18n.t('js.wiki_formatting.strong'), + 'Italic': I18n.t('js.wiki_formatting.italic'), + 'Underline': I18n.t('js.wiki_formatting.underline'), + 'Deleted': I18n.t('js.wiki_formatting.deleted'), + 'Code': I18n.t('js.wiki_formatting.code'), + 'Heading 1': I18n.t('js.wiki_formatting.heading1'), + 'Heading 2': I18n.t('js.wiki_formatting.heading2'), + 'Heading 3': I18n.t('js.wiki_formatting.heading3'), + 'Unordered list': I18n.t('js.wiki_formatting.unordered_list'), + 'Ordered list': I18n.t('js.wiki_formatting.ordered_list'), + 'Quote': I18n.t('js.wiki_formatting.quote'), + 'Unquote': I18n.t('js.wiki_formatting.unquote'), + 'Preformatted text': I18n.t('js.wiki_formatting.preformatted_text'), + 'Wiki link': I18n.t('js.wiki_formatting.wiki_link'), + 'Image': I18n.t('js.wiki_formatting.image') + } +}); + diff --git a/app/assets/stylesheets/content/_form_elements_input_textarea.md b/app/assets/stylesheets/content/_form_elements_input_textarea.md new file mode 100644 index 0000000000..631f7f4666 --- /dev/null +++ b/app/assets/stylesheets/content/_form_elements_input_textarea.md @@ -0,0 +1,18 @@ +# [Form elements] - Input, Textarea + +``` +
+ + +

+ +

+ +

+ +

+ + +
+ +``` diff --git a/app/assets/stylesheets/content/_form_elements_input_textarea.sass b/app/assets/stylesheets/content/_form_elements_input_textarea.sass new file mode 100644 index 0000000000..66a825bb63 --- /dev/null +++ b/app/assets/stylesheets/content/_form_elements_input_textarea.sass @@ -0,0 +1,34 @@ +/* Please use these styles for future purposes! The idea is to use these as global styles for form elements, after we finalised the work-packages view. The width of the elements may vary in different views */ + +#work-packages-index + input + border: 1px solid #cacaca + background: #ffffff + border-radius: 2px + padding: 8px + font-family: $font_family_normal + font-size: $global_font_size + box-sizing: border-box + &:hover + border: 1px solid #aaaaaa + &:focus + box-shadow: 1px 1px 1px #dddddd inset + .wide + width: 180px + .small + width: 90px + .form-textarea + margin: 0 0 10px 0 + textarea + border: 1px solid #cacaca + background: #ffffff + border-radius: 2px + padding: 8px + font-family: $font_family_normal + font-size: $global_font_size + width: 100% + box-sizing: border-box + &:hover + border: 1px solid #aaaaaa + &:focus + box-shadow: 1px 1px 1px #dddddd inset diff --git a/app/assets/stylesheets/content/_work_packages.sass b/app/assets/stylesheets/content/_work_packages.sass index e7d8b7ed4d..2a8691c250 100644 --- a/app/assets/stylesheets/content/_work_packages.sass +++ b/app/assets/stylesheets/content/_work_packages.sass @@ -269,16 +269,16 @@ div padding: 0 list-style-type: none li - margin: 0 0 30px 0 + margin: 0 0 25px 0 padding: 0 display: inline-table - min-width: 31% + min-width: 33% clear: left label font-weight: bold display: block - margin: 0 0 15px 0 + margin: 0 0 7px 0 &.detail-panel-attributes-toggler float: left margin: 10px 0 0 0 diff --git a/app/assets/stylesheets/default.css.sass b/app/assets/stylesheets/default.css.sass index 11adcaef38..7f591e993f 100644 --- a/app/assets/stylesheets/default.css.sass +++ b/app/assets/stylesheets/default.css.sass @@ -43,6 +43,7 @@ @import layout/all @import content/accounts @import content/forms +@import content/form_elements_input_textarea @import content/flash_messages @import content/calendar @import content/wiki diff --git a/app/assets/stylesheets/default_simple.css.sass b/app/assets/stylesheets/default_simple.css.sass index 81e545786d..fb0c04da82 100644 --- a/app/assets/stylesheets/default_simple.css.sass +++ b/app/assets/stylesheets/default_simple.css.sass @@ -35,8 +35,6 @@ @import fonts/lato @import fonts/openproject_icon_font -@import work_packages - @import layout/base @import layout/top_menu @import layout/breadcrumb @@ -46,6 +44,7 @@ @import content/accounts @import content/forms +@import content/form_elements_input_textarea @import content/flash_messages @import content/calendar @import content/wiki diff --git a/app/controllers/work_packages_controller.rb b/app/controllers/work_packages_controller.rb index b01a2f1378..d9ed8aa1d6 100644 --- a/app/controllers/work_packages_controller.rb +++ b/app/controllers/work_packages_controller.rb @@ -61,6 +61,10 @@ class WorkPackagesController < ApplicationController :protect_from_unauthorized_export, :only => [:index, :all, :preview] before_filter :load_query, :only => :index + DEFAULT_WORK_PACKAGE_PROPERTIES = [:status, :assignee, :responsible, + :date, :percentageDone, :priority, + :estimatedTime, :versionName, :spentTime] + def show respond_to do |format| format.html do @@ -179,7 +183,7 @@ class WorkPackagesController < ApplicationController flash[:notice] = l(:notice_successful_update) - show + redirect_to(work_package_path(work_package)) else edit end @@ -456,11 +460,11 @@ class WorkPackagesController < ApplicationController parse_preview_data_helper :work_package, [:notes, :description] end - def hook_overview_attributes - attributes = [] + def hook_overview_attributes(initial_attributes = DEFAULT_WORK_PACKAGE_PROPERTIES) + attributes = initial_attributes call_hook(:work_packages_overview_attributes, project: @project, attributes: attributes) - attributes + attributes.uniq end end diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb index 115152b872..44ffde3114 100644 --- a/app/helpers/journals_helper.rb +++ b/app/helpers/journals_helper.rb @@ -112,6 +112,7 @@ module JournalsHelper content << content_tag('div', format_text(journal, :notes, :attachments => attachments), :class => 'wikicontent', + :'ng-non-bindable' => '', "data-user" => journal.journable.author) css_classes = "wiki journal-notes" diff --git a/app/views/layouts/angular.html.erb b/app/views/layouts/angular.html.erb index 9b86e8df19..375c3b0917 100644 --- a/app/views/layouts/angular.html.erb +++ b/app/views/layouts/angular.html.erb @@ -46,6 +46,8 @@ See doc/COPYRIGHT.rdoc for more details. <%= include_gon %> <%= javascript_include_tag 'application' %> <%= javascript_include_tag 'angular-i18n/angular-locale_de-de' if I18n.locale == :de %> + +<%= call_hook :view_work_package_overview_attributes %> <%= user_specific_javascript_includes %> diff --git a/app/views/messages/_form.html.erb b/app/views/messages/_form.html.erb index c64756450b..a40786d888 100644 --- a/app/views/messages/_form.html.erb +++ b/app/views/messages/_form.html.erb @@ -51,9 +51,11 @@ See doc/COPYRIGHT.rdoc for more details. <% end %>

-<%= label_tag "message_content", l(:description_message_content), :class => "hidden-for-sighted" %> -<%= f.text_area :content, :cols => 80, :rows => 15, :class => 'wiki-edit', :id => 'message_content', - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>

+ <%= label_tag "message_content", l(:description_message_content), :class => "hidden-for-sighted" %> + <%= f.text_area :content, :cols => 80, :rows => 15, :class => 'wiki-edit', :id => 'message_content', + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '' %> +

<%= wikitoolbar_for 'message_content' %> diff --git a/app/views/messages/show.html.erb b/app/views/messages/show.html.erb index 98ef79013c..5f0e79135e 100644 --- a/app/views/messages/show.html.erb +++ b/app/views/messages/show.html.erb @@ -57,7 +57,7 @@ See doc/COPYRIGHT.rdoc for more details.

<%= authoring @topic.created_on, @topic.author %>

-
+
<%= format_text(@topic.content, :object => @topic, :attachments => @topic.attachments) %>
<%= link_to_attachments @topic, :author => false %> @@ -97,7 +97,7 @@ See doc/COPYRIGHT.rdoc for more details. :class => 'no-decoration-on-hover', :alt => l(:button_delete)) if message.destroyable_by?(User.current) %>
-
+
<%= format_text message, :content, :attachments => message.attachments %>
<%= link_to_attachments message, :author => false %> diff --git a/app/views/news/_form.html.erb b/app/views/news/_form.html.erb index 83369603ee..9515bd2a38 100644 --- a/app/views/news/_form.html.erb +++ b/app/views/news/_form.html.erb @@ -31,8 +31,11 @@ See doc/COPYRIGHT.rdoc for more details.

<%= f.text_field :title, :required => true, :size => 60 %>

<%= f.text_area :summary, :cols => 60, :rows => 2 %>

-

<%= f.text_area :description, :required => true, :cols => 60, :rows => 15, :class => 'wiki-edit', - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>

+

+ <%= f.text_area :description, :required => true, :cols => 60, :rows => 15, :class => 'wiki-edit', + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '' %> +

<%= wikitoolbar_for 'news_description' %> diff --git a/app/views/news/show.html.erb b/app/views/news/show.html.erb index 25ecdb939f..8be48a8d40 100644 --- a/app/views/news/show.html.erb +++ b/app/views/news/show.html.erb @@ -60,7 +60,7 @@ See doc/COPYRIGHT.rdoc for more details.

<% unless @news.summary.blank? %><%=h @news.summary %>
<% end %> <%= authoring @news.created_on, @news.author %>

-
+
<%= format_text(@news.description, :object => @news) %>

diff --git a/app/views/project_associations/_form.html.erb b/app/views/project_associations/_form.html.erb index 363aa12815..5be7a33263 100644 --- a/app/views/project_associations/_form.html.erb +++ b/app/views/project_associations/_form.html.erb @@ -32,5 +32,6 @@ See doc/COPYRIGHT.rdoc for more details. <%= f.text_area(:description, :class => 'timelines-project-association-description wiki-edit', :rows => 10, - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json)) %> + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '') %> <%= wikitoolbar_for 'project_association_description' %> diff --git a/app/views/project_associations/index.html.erb b/app/views/project_associations/index.html.erb index 14710ce2d4..3788c61d75 100644 --- a/app/views/project_associations/index.html.erb +++ b/app/views/project_associations/index.html.erb @@ -95,7 +95,7 @@ See doc/COPYRIGHT.rdoc for more details. - <% end %> - + <%= format_text association, :description %> diff --git a/app/views/projects/form/attributes/_description.html.erb b/app/views/projects/form/attributes/_description.html.erb index 9c93b81b2c..089ff18195 100644 --- a/app/views/projects/form/attributes/_description.html.erb +++ b/app/views/projects/form/attributes/_description.html.erb @@ -27,6 +27,9 @@ See doc/COPYRIGHT.rdoc for more details. ++#%> -

<%= form.text_area :description, :rows => 5, :class => 'wiki-edit', - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>

+

+ <%= form.text_area :description, :rows => 5, :class => 'wiki-edit', + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '' %> +

<%= wikitoolbar_for 'project_description' %> diff --git a/app/views/projects/form/attributes/_summary.html.erb b/app/views/projects/form/attributes/_summary.html.erb index 8ec0327d3d..9ac1017a7b 100644 --- a/app/views/projects/form/attributes/_summary.html.erb +++ b/app/views/projects/form/attributes/_summary.html.erb @@ -27,5 +27,8 @@ See doc/COPYRIGHT.rdoc for more details. ++#%> -

<%= form.text_area :summary, :rows => 2, :class => 'wiki-edit', - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>

+

+ <%= form.text_area :summary, :rows => 2, :class => 'wiki-edit', + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '' %> +

diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index f0f417e2da..bce111d50b 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -38,7 +38,7 @@ See doc/COPYRIGHT.rdoc for more details. <% breadcrumb_paths(l(:label_overview)) %>
-
+
<%= format_text @project.description %>
    diff --git a/app/views/reportings/edit.html.erb b/app/views/reportings/edit.html.erb index fe67a0ae09..5c63878a55 100644 --- a/app/views/reportings/edit.html.erb +++ b/app/views/reportings/edit.html.erb @@ -68,7 +68,8 @@ See doc/COPYRIGHT.rdoc for more details. <%= Reporting.human_attribute_name(:reported_project_status_comment) %>:
    <%= f.text_area(:reported_project_status_comment, :class => 'wiki-edit', :rows => 10, - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json)) %> + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '') %> <%= wikitoolbar_for 'reporting_reported_project_status_comment' %>

    diff --git a/app/views/reportings/index.html.erb b/app/views/reportings/index.html.erb index ab2b387680..8bc31a2170 100644 --- a/app/views/reportings/index.html.erb +++ b/app/views/reportings/index.html.erb @@ -65,7 +65,7 @@ See doc/COPYRIGHT.rdoc for more details. <%=h reporting.reported_project_status.try(:name) || "-" %> - + <%= format_text reporting, :reported_project_status_comment %> diff --git a/app/views/settings/_general.html.erb b/app/views/settings/_general.html.erb index 9735606806..7d042352f4 100644 --- a/app/views/settings/_general.html.erb +++ b/app/views/settings/_general.html.erb @@ -32,8 +32,10 @@ See doc/COPYRIGHT.rdoc for more details.

    <%= setting_text_field :app_title, :size => 30 %>

    -

    <%= setting_text_area :welcome_text, :cols => 60, :rows => 5, :class => 'wiki-edit' %>

    -<%= wikitoolbar_for 'settings_welcome_text' %> +

    + <%= setting_text_area :welcome_text, :cols => 60, :rows => 5, :class => 'wiki-edit', :'ng-non-bindable' => '' %> + <%= wikitoolbar_for 'settings_welcome_text' %> +

    <%= setting_text_field :attachment_max_size, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %>

    diff --git a/app/views/wiki/edit.html.erb b/app/views/wiki/edit.html.erb index 151cd40668..ad18786776 100644 --- a/app/views/wiki/edit.html.erb +++ b/app/views/wiki/edit.html.erb @@ -33,9 +33,12 @@ See doc/COPYRIGHT.rdoc for more details. <%= f.hidden_field :lock_version %> <%= error_messages_for 'content' %> -

    <%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit), - :value => format_text(@content, :text, :attachments => @content.page.attachments, :edit => true), - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>

    +

    + <%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit), + :value => format_text(@content, :text, :attachments => @content.page.attachments, :edit => true), + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '' %> +


    <%= f.text_field :comments, :size => 120 %>


    <%= render :partial => 'attachments/form' %>

    <%= submit_tag l(:button_save) %> diff --git a/app/views/wiki/new.html.erb b/app/views/wiki/new.html.erb index 21b4494452..1f7e109747 100644 --- a/app/views/wiki/new.html.erb +++ b/app/views/wiki/new.html.erb @@ -53,7 +53,8 @@ See doc/COPYRIGHT.rdoc for more details.

    <%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit), - :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %> + :'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json), + :'ng-non-bindable' => '' %>

    diff --git a/app/views/work_packages/show.html.erb b/app/views/work_packages/show.html.erb index fb079c4cd5..029e1c8823 100644 --- a/app/views/work_packages/show.html.erb +++ b/app/views/work_packages/show.html.erb @@ -58,7 +58,7 @@ See doc/COPYRIGHT.rdoc for more details. <%= link_to_if_authorized(l(:button_quote), { controller: :work_packages, action: :quoted, id: work_package }, :class => 'quote-link icon icon-quote') %>

    <%= WorkPackage.human_attribute_name(:description)%>

    -
    +
    <%= format_text work_package, :description, :attachments => work_package.attachments %>
    diff --git a/bower.json b/bower.json index 18d6287a9d..780b5fa84a 100644 --- a/bower.json +++ b/bower.json @@ -22,7 +22,7 @@ "jquery-migrate": "~1.2.1", "momentjs": "~2.7.0", "moment-timezone": "~0.2.0", - "angular-context-menu": "0.1.2", + "angular-context-menu": "finnlabs/angular-context-menu#v0.1.4", "angular-busy": "~4.0.4", "hyperagent": "manwithtwowatches/hyperagent#v0.4.2" }, diff --git a/config/locales/de.yml b/config/locales/de.yml index ec31eb9c16..46c57868b9 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -33,8 +33,8 @@ de: deleted: "Account erfolgreich gelöscht" deletion_info: data_consequences: - other: "Von den vom Nutzer im Zuge der Nutzung der Anwendung erstellen Daten (z.B. E-Mail-Adresse, Systemeinstellungen, Tickets, Wiki-Einträgen) werden so viele wie möglich gelöscht. Bitte beachten Sie allerdings, dass Daten wie Tickets und Wiki-Einträge nicht gelöscht werden können, da dies die Arbeit der anderen Nutzer behindern würde. Solche Daten werden daher einem \"Gelöschten Nutzer\" zugewiesen. Da die Daten aller gelöschter Accounts dem \"Gelöschten Nutzer\" zugewiesen werden, ist es unmöglich, die vom Nutzer erstellten Daten diesem Nutzer zuzuordnen." - self: "Von den von Ihnen im Zuge der Nutzung der Anwendung erstellen Daten (z.B. E-Mail-Adresse, Systemeinstellungen, Tickets, Wiki-Einträgen) werden so viele wie möglich gelöscht. Bitte beachten Sie allerdings, dass Daten wie Tickets und Wiki-Einträge nicht gelöscht werden können, da dies die Arbeit der anderen Nutzer behindern würde. Solche Daten werden daher einem \"Gelöschten Nutzer\" zugewiesen. Da die Daten aller gelöschter Accounts dem \"Gelöschten Nutzer\" zugewiesen werden, ist es unmöglich, die von Ihnen erstellten Daten auf Ihre Person zurückzuführen." + other: "Von den vom Nutzer im Zuge der Nutzung der Anwendung erstellen Daten (z.B. E-Mail-Adresse, Systemeinstellungen, Arbeitspaketen, Wiki-Einträgen) werden so viele wie möglich gelöscht. Bitte beachten Sie allerdings, dass Daten wie Arbeitspakete und Wiki-Einträge nicht gelöscht werden können, da dies die Arbeit der anderen Nutzer behindern würde. Solche Daten werden daher einem \"Gelöschten Nutzer\" zugewiesen. Da die Daten aller gelöschter Accounts dem \"Gelöschten Nutzer\" zugewiesen werden, ist es unmöglich, die vom Nutzer erstellten Daten diesem Nutzer zuzuordnen." + self: "Von den von Ihnen im Zuge der Nutzung der Anwendung erstellen Daten (z.B. E-Mail-Adresse, Systemeinstellungen, Arbeitspakete, Wiki-Einträgen) werden so viele wie möglich gelöscht. Bitte beachten Sie allerdings, dass Daten wie Arbeitspakete und Wiki-Einträge nicht gelöscht werden können, da dies die Arbeit der anderen Nutzer behindern würde. Solche Daten werden daher einem \"Gelöschten Nutzer\" zugewiesen. Da die Daten aller gelöschter Accounts dem \"Gelöschten Nutzer\" zugewiesen werden, ist es unmöglich, die von Ihnen erstellten Daten auf Ihre Person zurückzuführen." heading: "Lösche Account %{name}" info: other: "Das Löschen des Accounts kann nicht rückgängig gemacht werden." @@ -115,11 +115,11 @@ de: repository: url: "URL" role: - assignable: "Tickets können dieser Rolle zugewiesen werden" + assignable: "Arbeitspakete können dieser Rolle zugewiesen werden" time_entry: activity: "Aktivität" hours: "Stunden" - issue: "Ticket" + issue: "Arbeitspaket" spent_on: "Datum" type: "Typ" type_color: @@ -263,7 +263,7 @@ de: comment: "Kommentar" custom_field: "Benutzerdefiniertes Feld" group: "Gruppe" - issue: "Ticket" + issue: "Arbeitspaket" category: "Kategorie" status: "Arbeitspaket-Status" member: "Mitglied" @@ -349,7 +349,7 @@ de: button_collapse_all: "Alle zuklappen" button_configure: "Konfigurieren" button_copy: "Kopieren" - button_copy_and_follow: "Kopieren und Ticket anzeigen" + button_copy_and_follow: "Kopieren und Arbeitspaket anzeigen" button_create: "Anlegen" button_create_and_continue: "Anlegen und weiter" button_delete: "Löschen" @@ -364,7 +364,7 @@ de: button_log_time: "Aufwand buchen" button_login: "Anmelden" button_move: "Verschieben" - button_move_and_follow: "Verschieben und Ticket anzeigen" + button_move_and_follow: "Verschieben und Arbeitspaket anzeigen" button_quote: "Zitieren" button_remove_widget: "Infobox löschen" button_rename: "Umbenennen" @@ -456,6 +456,10 @@ de: x_seconds: one: "1 Sekunde" other: "%{count} Sekunden" + units: + hour: + one: "Stunde" + other: "Stunden" default_activity_design: "Design" default_activity_development: "Entwicklung" @@ -486,10 +490,10 @@ de: description_attachment_toggle: "Dateien aus/einblenden" description_autocomplete: > Für dieses Feld werden Sie mit einer Autovervollständigung - unterstützt. Sie können einen Teil des Tickettitels schreiben und - bekommen eine Liste von möglichen Tickets angezeigt. Wählen Sie mit + unterstützt. Sie können einen Teil des Arbeitspakettitels schreiben und + bekommen eine Liste von möglichen Arbeitspaketen angezeigt. Wählen Sie mit den Pfeiltasten den gewünschten Eintrag und bestätigen Sie mit Tab - oder Enter. Sie können aber auch die Ticketnummer direkt eintragen. + oder Enter. Sie können aber auch die Arbeitspaketnummer direkt eintragen. description_available_columns: "Verfügbare Spalten" description_choose_project: "Projekte" description_compare_from: "Vergleiche von Version" @@ -529,7 +533,7 @@ de: error_can_not_archive_project: "Dieses Projekt kann nicht archiviert werden." error_can_not_delete_custom_field: "Kann das benutzerdefinierte Feld nicht löschen." - error_can_not_delete_type: "Dieser Typ enthält Tickets und kann nicht gelöscht werden." + error_can_not_delete_type: "Dieser Typ enthält Arbeitspakete und kann nicht gelöscht werden." error_can_not_delete_standard_type: "Standardtypen können nicht gelöscht werden." error_can_not_remove_role: "Diese Rolle wird verwendet und kann nicht gelöscht werden." error_can_not_reopen_work_package_on_closed_version: "Das Arbeitspaket ist einer abgeschlossenen Version zugeordnet und kann daher nicht wieder geöffnet werden." @@ -538,13 +542,13 @@ de: error_work_package_done_ratios_not_updated: "Der Arbeitspaket-Fortschritt wurde nicht aktualisiert." error_work_package_not_found_in_project: "Das Arbeitspaket wurde nicht gefunden oder gehört nicht zu diesem Projekt." error_must_be_project_member: "muss Mitglied des Projekts sein" - error_no_default_work_package_status: "Es ist kein Status als Standard definiert. Bitte überprüfen Sie Ihre Konfiguration (unter \"Administration -> Ticket-Status\")." + error_no_default_work_package_status: "Es ist kein Status als Standard definiert. Bitte überprüfen Sie Ihre Konfiguration (unter \"Administration -> Arbeitspaket-Status\")." error_no_type_in_project: "Diesem Projekt ist kein Typ zugeordnet. Bitte überprüfen Sie die Projekteinstellungen." error_omniauth_registration_timed_out: "Die Registrierung über einen externen Authentifizierungsprovider ist fehlgeschlagen. Bitte versuchen Sie es erneut." error_scm_annotate: "Der Eintrag existiert nicht oder kann nicht annotiert werden." error_scm_command_failed: "Beim Zugriff auf das Projektarchiv ist ein Fehler aufgetreten: %{value}" error_scm_not_found: "Eintrag und/oder Revision existiert nicht im Projektarchiv." - error_unable_delete_status: "Der Ticket-Status konnte nicht gelöscht werden." + error_unable_delete_status: "Der Arbeitspaket-Status konnte nicht gelöscht werden." error_unable_delete_default_status: "Der Arbeitspaket-Status konnte nicht gelöscht werden, da dieser als Standard-Status markiert ist. Wählen Sie bitte zunächst einen anderen Arbeitspaket-Status als Standard-Status." error_unable_to_connect: "Fehler beim Verbinden (%{value})" error_workflow_copy_source: "Bitte wählen Sie einen Quell-Typ und eine Quell-Rolle." @@ -589,7 +593,7 @@ de: label_add_another_file: "Eine weitere Datei hinzufügen" label_add_columns: "Ausgewählte Spalten hinzufügen" label_add_note: "Kommentar hinzufügen" - label_add_related_issues: "Zugehöriges Ticket hinzufügen" + label_add_related_issues: "Zugehöriges Arbeitspaket hinzufügen" label_add_related_work_packages: "Zugehöriges Arbeitspaket hinzufügen" label_add_subtask: "Unteraufgabe hinzufügen" label_added: "hinzugefügt" @@ -745,7 +749,7 @@ de: label_work_package_status_plural: "Arbeitspaket-Status" label_work_package_tracking: "Arbeitspakete" label_work_package_updated: "Arbeitspaket aktualisiert" - label_work_package_view_all: "Alle Tickets anzeigen" + label_work_package_view_all: "Alle Arbeitspakete anzeigen" label_work_package_view_all_assigned_to_me: "Alle mir zugewiesenen Arbeitspakete anzeigen" label_work_package_view_all_reported_by_me: "Alle von mir erstellten Arbeitspakete anzeigen" label_work_package_view_all_responsible_for: "Alle von mir verantworteten Arbeitspakete anzeigen" @@ -1040,13 +1044,13 @@ de: mail_body_account_information_external: "Sie können sich mit Ihrem Konto %{value} anmelden." mail_body_lost_password: "Benutzen Sie den folgenden Link, um Ihr Kennwort zu ändern:" mail_body_register: "Um Ihr Konto zu aktivieren, benutzen Sie folgenden Link:" - mail_body_reminder: "%{count} Tickets, die Ihnen zugewiesen sind, müssen in den nächsten %{days} Tagen abgegeben werden:" + mail_body_reminder: "%{count} Arbeitspakete, die Ihnen zugewiesen sind, müssen in den nächsten %{days} Tagen abgegeben werden:" mail_body_wiki_content_added: "Die Wiki-Seite '%{id}' wurde von %{author} hinzugefügt." mail_body_wiki_content_updated: "Die Wiki-Seite '%{id}' wurde von %{author} aktualisiert." mail_subject_account_activation_request: "Antrag auf %{value} Kontoaktivierung" mail_subject_lost_password: "Ihr %{value} Kennwort" mail_subject_register: "%{value} Kontoaktivierung" - mail_subject_reminder: "%{count} Tickets müssen in den nächsten %{days} Tagen abgegeben werden" + mail_subject_reminder: "%{count} Arbeitspakete müssen in den nächsten %{days} Tagen abgegeben werden" mail_subject_wiki_content_added: "Wiki-Seite '%{id}' hinzugefügt" mail_subject_wiki_content_updated: "Wiki-Seite '%{id}' erfolgreich aktualisiert" @@ -1168,7 +1172,7 @@ de: permission_list_attachments: "Anhänge auflisten" permission_log_time: "Aufwände buchen" permission_manage_boards: "Foren verwalten" - permission_manage_categories: "Ticket-Kategorien verwalten" + permission_manage_categories: "Arbeitspaket-Kategorien verwalten" permission_manage_work_package_relations: "Arbeitspaket-Beziehungen verwalten" permission_manage_members: "Mitglieder verwalten" permission_manage_news: "News verwalten" @@ -1227,7 +1231,7 @@ de: setting_brute_force_block_after_failed_logins: "Sperre Benutzer nach dieser Anzahl fehlgeschlagener Loginversuche (deaktivieren mit 0)" setting_brute_force_block_minutes: "Sperre Benutzer für Dauer (Minuten)" setting_cache_formatted_text: "Formatierten Text im Cache speichern" - setting_column_options: "Anpassen der Darstellung der Ticketlisten" + setting_column_options: "Anpassen der Darstellung der Arbeitspaketlisten" setting_commit_fix_keywords: "Schlüsselwörter (Status)" setting_commit_logs_encoding: "Kodierung der Commit-Log-Meldungen" setting_commit_logtime_activity_id: "Aktivität für die Zeiterfassung" @@ -1353,7 +1357,7 @@ de: text_load_default_configuration: "Standard-Konfiguration laden" text_min_max_length_info: "0 heißt keine Beschränkung" text_no_configuration_data: > - Rollen, Typ, Ticket-Status und Workflows wurden noch nicht + Rollen, Typ, Arbeitspaket-Status und Workflows wurden noch nicht konfiguriert. Es ist sehr zu empfehlen, die Standard-Konfiguration zu laden. Sobald sie geladen ist, können Sie sie abändern. text_own_membership_delete_confirmation: "Sie sind dabei, einige oder alle Ihre Berechtigungen zu entfernen. Es ist möglich, dass Sie danach das Projekt nicht mehr ansehen oder bearbeiten dürfen.\nSind Sie sicher, dass Sie dies tun möchten?" @@ -1375,7 +1379,7 @@ de: text_tip_work_package_end_day: "Arbeitspaket, das an diesem Tag endet" text_type_no_workflow: "Kein Workflow für diesen Typ definiert." text_unallowed_characters: "Nicht erlaubte Zeichen" - text_user_mail_option: "Für nicht ausgewählte Projekte werden Sie nur Benachrichtigungen für Dinge erhalten, die Sie beobachten oder an denen Sie beteiligt sind (z. B. Tickets, deren Autor Sie sind oder die Ihnen zugewiesen sind)." + text_user_mail_option: "Für nicht ausgewählte Projekte werden Sie nur Benachrichtigungen für Dinge erhalten, die Sie beobachten oder an denen Sie beteiligt sind (z. B. Arbeitspakete, deren Autor Sie sind oder die Ihnen zugewiesen sind)." text_user_wrote: "%{value} schrieb:" text_warn_on_leaving_unsaved: "Der eingegebene Text wird nicht gespeichert, wenn Sie die Seite jetzt verlassen." text_wiki_destroy_confirmation: "Sind Sie sicher, dass Sie dieses Wiki mit sämtlichem Inhalt löschen möchten?" @@ -1622,6 +1626,7 @@ de: menu_item: "Menüpunkt" menu_item_setting: "Sichtbarkeit" + wiki_menu_item_for: "Menüpunkt für die Wikiseite \"%{title}\"" wiki_menu_item_setting: "Sichtbarkeit" wiki_menu_item_new_main_item_explanation: > diff --git a/config/locales/en.yml b/config/locales/en.yml index f8a81555ae..4465f9abf0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -454,6 +454,10 @@ en: x_seconds: one: "1 second" other: "%{count} seconds" + units: + hour: + one: "hour" + other: "hours" default_activity_design: "Design" default_activity_development: "Development" @@ -1612,6 +1616,7 @@ en: menu_item: "Menu item" menu_item_setting: "Visibility" + wiki_menu_item_for: "Menu item for wikipage \"%{title}\"" wiki_menu_item_setting: "Visibility" wiki_menu_item_new_main_item_explanation: > diff --git a/config/locales/js-de.yml b/config/locales/js-de.yml index 19d61287da..6a09e2a779 100644 --- a/config/locales/js-de.yml +++ b/config/locales/js-de.yml @@ -41,6 +41,7 @@ de: button_edit: "Bearbeiten" button_log_time: "Aufwand buchen" button_move: "Verschieben" + button_open_details: "Öffne Detailansicht" button_quote: "Zitieren" button_save: "Speichern" button_uncheck_all: "Alles abwählen" @@ -242,6 +243,22 @@ de: title: "Ihr Browser wird nicht unterstützt" message: "Sie verwenden einen veralteten Browser. OpenProject unterstützt diesen Browser nicht länger. Bitte aktualisieren Sie Ihren Browser." learn_more: "Mehr erfahren" + wiki_formatting: + strong: "Fett" + italic: "Kursiv" + underline: "Unterstrichen" + deleted: "Duchgestrichen" + code: "Quelltext" + heading1: "Überschrift 1. Ordnung" + heading2: "Überschrift 2. Ordnung" + heading3: "Überschrift 3. Ordnung" + unordered_list: "Aufzählungsliste" + ordered_list: "Nummerierte Liste" + quote: "Zitieren" + unquote: "Zitat entfernen" + preformatted_text: "Präformatierter Text" + wiki_link: "Verweis (Link) zu einer Wiki-Seite" + image: "Grafik" work_packages: button_clear: "Zurücksetzen" description_filter: "Filter" diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index ee0e8388e4..342ccade77 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -41,6 +41,7 @@ en: button_edit: "Edit" button_log_time: "Log time" button_move: "Move" + button_open_details: "Open details view" button_quote: "Quote" button_save: "Save" button_uncheck_all: "Uncheck all" @@ -244,6 +245,22 @@ en: title: "Your browser is not supported" message: "The browser you are using is no longer supported by OpenProject. Please update your browser." learn_more: "Learn more" + wiki_formatting: + strong: "Strong" + italic: "Italic" + underline: "Underline" + deleted: "Deleted" + code: "Inline Code" + heading1: "Heading 1" + heading2: "Heading 2" + heading3: "Heading 3" + unordered_list: "Unordered List" + ordered_list: "Ordered List" + quote: "Quote" + unquote: "Unquote" + preformatted_text: "Preformatted Text" + wiki_link: "Link to a Wiki page" + image: "Image" work_packages: button_clear: "Clear" description_filter: "Filter" diff --git a/karma/tests/controllers/details-tab-overview-controller-test.js b/karma/tests/controllers/details-tab-overview-controller-test.js index 774b99f928..a860b77e84 100644 --- a/karma/tests/controllers/details-tab-overview-controller-test.js +++ b/karma/tests/controllers/details-tab-overview-controller-test.js @@ -29,8 +29,14 @@ /*jshint expr: true*/ describe('DetailsTabOverviewController', function() { + var DEFAULT_WORK_PACKAGE_PROPERTIES = ['status', 'assignee', 'responsible', + 'date', 'percentageDone', 'priority', + 'estimatedTime', 'versionName', 'spentTime'] + var scope; var buildController; + var HookService; + var ConfigurationService; var I18n = { t: angular.identity }, WorkPackagesHelper = { formatWorkPackageProperty: angular.identity @@ -57,6 +63,7 @@ describe('DetailsTabOverviewController', function() { attachments: [] }, }; + var workPackageAttributesStub; function buildWorkPackageWithId(id) { angular.extend(workPackage.props, {id: id}); @@ -68,7 +75,7 @@ describe('DetailsTabOverviewController', function() { 'openproject.config', 'openproject.workPackages.controllers')); - beforeEach(inject(function($rootScope, $controller, $timeout) { + beforeEach(inject(function($rootScope, $controller, $timeout, _HookService_, _ConfigurationService_) { var workPackageId = 99; buildController = function() { @@ -85,6 +92,11 @@ describe('DetailsTabOverviewController', function() { $timeout.flush(); }; + HookService = _HookService_; + ConfigurationService = _ConfigurationService_; + + workPackageAttributesStub = sinon.stub(ConfigurationService, "workPackageAttributes"); + workPackageAttributesStub.returns(DEFAULT_WORK_PACKAGE_PROPERTIES); })); describe('initialisation', function() { @@ -330,7 +342,41 @@ describe('DetailsTabOverviewController', function() { }); }); }); - }); + describe('Plug-in properties', function() { + var propertyName = 'myPluginProperty'; + var directiveName = 'my-plugin-property-directive'; + + beforeEach(function() { + gon.settings = { }; + gon.settings.work_package_attributes = [propertyName]; + + var attributes = DEFAULT_WORK_PACKAGE_PROPERTIES.slice(0); + attributes.push(propertyName); + + workPackageAttributesStub.returns(attributes); + + var workPackageOverviewAttributesStub = sinon.stub(HookService, "call"); + workPackageOverviewAttributesStub.withArgs('workPackageOverviewAttributes', + { type: propertyName, + workPackage: workPackage }) + .returns([directiveName]); + workPackageOverviewAttributesStub.returns([]); + + buildController(); + }); + + it('adds plug-in property to present properties', function() { + expect(fetchPresentPropertiesWithName(propertyName)).to.have.length(1); + }); + it('adds plug-in property to present properties', function() { + var propertyData = fetchPresentPropertiesWithName(propertyName)[0]; + + expect(propertyData.property).to.eq(propertyName); + expect(propertyData.format).to.eq('dynamic'); + expect(propertyData.value).to.eq(directiveName); + }); + }); + }); }); diff --git a/karma/tests/controllers/work-package-details-controller-test.js b/karma/tests/controllers/work-package-details-controller-test.js index 8a89cc6667..9f99b59225 100644 --- a/karma/tests/controllers/work-package-details-controller-test.js +++ b/karma/tests/controllers/work-package-details-controller-test.js @@ -50,6 +50,12 @@ describe('WorkPackageDetailsController', function() { ] }, embedded: { + author: { + props: { + id: 1, + status: 1 + } + }, activities: [], watchers: [], attachments: [], diff --git a/karma/tests/directives/components/focus-test.js b/karma/tests/directives/components/focus-test.js index 1f492c6820..6504fb48fe 100644 --- a/karma/tests/directives/components/focus-test.js +++ b/karma/tests/directives/components/focus-test.js @@ -32,7 +32,7 @@ describe('focus Directive', function() { beforeEach(angular.mock.module('openproject.uiComponents')); beforeEach(module('templates')); - beforeEach(inject(function($compile, $rootScope, $document) { + beforeEach(inject(function($compile, $rootScope, $document, $timeout) { var html = ''; doc = $document[0]; @@ -46,6 +46,8 @@ describe('focus Directive', function() { $compile(element)(scope); scope.$digest(); + + $timeout.flush(); }; })); diff --git a/karma/tests/services/hook-service-test.js b/karma/tests/services/hook-service-test.js new file mode 100644 index 0000000000..7f54a0e004 --- /dev/null +++ b/karma/tests/services/hook-service-test.js @@ -0,0 +1,176 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2014 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. +//++ + +/*jshint expr: true*/ + +describe('HookService', function() { + + var HookService; + var validId = 'myValidCallbacks'; + + beforeEach(module('openproject.services')); + + beforeEach(inject(function(_HookService_){ + HookService = _HookService_; + })); + + var shouldBehaveLikeEmptyResult = function(id) { + it('returns empty results', function() { + expect(HookService.call(id)).to.be.empty; + }); + }; + + var shouldBehaveLikeResultWithElements = function(id, count) { + it('returns #count results', function() { + expect(HookService.call(id).length).to.eq(count); + }); + }; + + var shouldBehaveLikeCalledCallback = function(id) { + beforeEach(function() { + HookService.call(id); + }); + + it('is called', function() { + expect(callback.called).to.be.true; + }); + }; + + var shouldBehaveLikeUncalledCallback = function(id) { + beforeEach(function() { + HookService.call(id); + }); + + it('is not called', function() { + expect(invalidCallback.called).to.be.false; + }); + }; + + describe('register', function() { + var invalidId = 'myInvalidCallbacks'; + + describe('no callback registered', function() { + shouldBehaveLikeEmptyResult(invalidId); + }); + + describe('undefined callback registered', function() { + beforeEach(function() { + HookService.register('myInvalidCallbacks'); + }); + + shouldBehaveLikeEmptyResult(invalidId); + }); + + describe('non function callback registered', function() { + beforeEach(function() { + HookService.register('myInvalidCallbacks', 'eeek'); + }); + + shouldBehaveLikeEmptyResult(invalidId); + }); + + describe('valid function callback registered', function() { + beforeEach(function() { + callback = sinon.spy(); + HookService.register('myValidCallbacks', callback); + }); + + shouldBehaveLikeEmptyResult(validId); + + shouldBehaveLikeCalledCallback(validId); + }); + }); + + describe('call', function() { + describe('function that returns undefined', function() { + beforeEach(function() { + callback = sinon.spy(); + HookService.register('myValidCallbacks', callback); + }); + + shouldBehaveLikeCalledCallback(validId); + + shouldBehaveLikeEmptyResult(validId); + }); + + describe('function that returns something that is not undefined', function() { + beforeEach(function() { + callback = sinon.stub(); + callback.returns(new Object()); + + HookService.register('myValidCallbacks', callback); + }); + + shouldBehaveLikeCalledCallback(validId); + + shouldBehaveLikeResultWithElements(validId, 1); + }); + + describe('function that returns something that is not undefined', function() { + beforeEach(function() { + callback = sinon.stub(); + callback.returns(new Object()); + + HookService.register('myValidCallbacks', callback); + }); + + shouldBehaveLikeCalledCallback(validId); + + shouldBehaveLikeResultWithElements(validId, 1); + }); + + describe('function that returns something that is not undefined', function() { + beforeEach(function() { + callback = sinon.spy(); + invalidCallback = sinon.spy(); + + HookService.register('myValidCallbacks', callback); + + HookService.register('myInvalidCallbacks', invalidCallback); + }); + + shouldBehaveLikeCalledCallback(validId); + + shouldBehaveLikeUncalledCallback(validId); + }); + + describe('function that returns something that is not undefined', function() { + beforeEach(function() { + callback1 = sinon.stub(); + callback1.returns(new Object()); + callback2 = sinon.stub(); + callback2.returns(new Object()); + + HookService.register('myValidCallbacks', callback1); + HookService.register('myValidCallbacks', callback2); + }); + + shouldBehaveLikeResultWithElements(validId, 2); + }); + }); +}); diff --git a/karma/tests/work_packages/work-package-context-menu-test.js b/karma/tests/work_packages/work-package-context-menu-test.js index a1b224544f..bc5bb50dfc 100644 --- a/karma/tests/work_packages/work-package-context-menu-test.js +++ b/karma/tests/work_packages/work-package-context-menu-test.js @@ -100,11 +100,11 @@ describe('workPackageContextMenu', function() { }); it('lists link tags for any permitted action', function(){ - expect(directListElements.length).to.equal(2); + expect(directListElements.length).to.equal(3); }); it('assigns a css class named by the action', function(){ - expect(directListElements[0].className).to.equal(actions[0]); + expect(directListElements[1].className).to.equal(actions[0]); }); it('adds an icon from the icon fonts to each list container', function() { @@ -139,7 +139,7 @@ describe('workPackageContextMenu', function() { }); it('displays a link triggering deleteWorkPackages within the scope', function() { - expect(directListElements.find('a').attr('ng-click')).to.equal('deleteWorkPackages()'); + expect(directListElements.find('a.icon-delete').attr('ng-click')).to.equal('deleteWorkPackages()'); }); }); }); diff --git a/lib/api/root.rb b/lib/api/root.rb index 39917f4c48..1c9274d4ae 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -34,8 +34,17 @@ module API class Root < Grape::API prefix :api + + class Formatter + def call(object, env) + object.respond_to?(:to_json) ? object.to_json : MultiJson.dump(object) + end + end + content_type 'hal+json', 'application/hal+json' + content_type :json, 'application/json' format 'hal+json' + formatter 'hal+json', Formatter.new helpers do def current_user @@ -56,7 +65,7 @@ module API def build_representer(obj, model_klass, representer_klass, options = {}) model = (obj.kind_of?(Array)) ? obj.map{ |o| model_klass.new(o) } : model_klass.new(obj) - representer_klass.new(model, options).to_json + representer_klass.new(model, options) end end diff --git a/lib/api/v3/activities/activities_api.rb b/lib/api/v3/activities/activities_api.rb index 522cedc3c3..201239b594 100644 --- a/lib/api/v3/activities/activities_api.rb +++ b/lib/api/v3/activities/activities_api.rb @@ -46,7 +46,7 @@ module API get do authorize(:view_project, context: @activity.journable.project) - @representer.to_json + @representer end helpers do @@ -55,7 +55,7 @@ module API model = ::API::V3::Activities::ActivityModel.new(activity) representer = ::API::V3::Activities::ActivityRepresenter.new(model) - representer.to_json + representer else errors = activity.errors.full_messages.join(", ") fail Errors::Validation.new(activity, description: errors) diff --git a/lib/api/v3/activities/activity_representer.rb b/lib/api/v3/activities/activity_representer.rb index 71e45c08d6..bdb2133c5d 100644 --- a/lib/api/v3/activities/activity_representer.rb +++ b/lib/api/v3/activities/activity_representer.rb @@ -49,20 +49,20 @@ module API property :_type, exec_context: :decorator link :self do - { href: "#{root_url}api/v3/activities/#{represented.model.id}", title: "#{represented.model.id}" } + { href: "#{root_path}api/v3/activities/#{represented.model.id}", title: "#{represented.model.id}" } end link :workPackage do - { href: "#{root_url}api/v3/work_packages/#{represented.model.journable.id}", title: "#{represented.model.journable.subject}" } + { href: "#{root_path}api/v3/work_packages/#{represented.model.journable.id}", title: "#{represented.model.journable.subject}" } end link :user do - { href: "#{root_url}api/v3/users/#{represented.model.user.id}", title: "#{represented.model.user.name} - #{represented.model.user.login}" } + { href: "#{root_path}api/v3/users/#{represented.model.user.id}", title: "#{represented.model.user.name} - #{represented.model.user.login}" } end link :update do { - href: "#{root_url}api/v3/activities/#{represented.model.id}", + href: "#{root_path}api/v3/activities/#{represented.model.id}", method: :patch, title: "#{represented.model.id}" } if current_user_allowed_to_edit? diff --git a/lib/api/v3/attachments/attachment_representer.rb b/lib/api/v3/attachments/attachment_representer.rb index 2961ac928e..83d125d848 100644 --- a/lib/api/v3/attachments/attachment_representer.rb +++ b/lib/api/v3/attachments/attachment_representer.rb @@ -43,17 +43,17 @@ module API property :_type, exec_context: :decorator link :self do - { href: "#{root_url}api/v3/attachments/#{represented.model.id}", title: "#{represented.model.filename}" } + { href: "#{root_path}api/v3/attachments/#{represented.model.id}", title: "#{represented.model.filename}" } end link :work_package do work_package = represented.model.container - { href: "#{root_url}api/v3/work_packages/#{work_package.id}", title: "#{work_package.subject}" } unless work_package.nil? + { href: "#{root_path}api/v3/work_packages/#{work_package.id}", title: "#{work_package.subject}" } unless work_package.nil? end link :author do author = represented.model.author - { href: "#{root_url}api/v3/users/#{author.id}", title: "#{author.name} - #{author.login}" } unless author.nil? + { href: "#{root_path}api/v3/users/#{author.id}", title: "#{author.name} - #{author.login}" } unless author.nil? end property :id, getter: -> (*) { model.id }, render_nil: true diff --git a/lib/api/v3/attachments/attachments_api.rb b/lib/api/v3/attachments/attachments_api.rb index 4aa7c963b0..e5e056f73e 100644 --- a/lib/api/v3/attachments/attachments_api.rb +++ b/lib/api/v3/attachments/attachments_api.rb @@ -46,7 +46,7 @@ module API get do authorize(:view_project, context: @attachment.container.project) - @representer.to_json + @representer end end diff --git a/lib/api/v3/priorities/priorities_api.rb b/lib/api/v3/priorities/priorities_api.rb new file mode 100644 index 0000000000..fd45646904 --- /dev/null +++ b/lib/api/v3/priorities/priorities_api.rb @@ -0,0 +1,49 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +module API + module V3 + module Priorities + class PrioritiesAPI < Grape::API + + resources :priorities do + before do + @priorities = IssuePriority.all + @priorities.map! { |priority| PriorityModel.new(priority) } + end + + get do + PriorityCollectionRepresenter.new(@priorities) + end + end + + end + end + end +end diff --git a/lib/api/v3/priorities/priority_collection_representer.rb b/lib/api/v3/priorities/priority_collection_representer.rb new file mode 100644 index 0000000000..d2d4b1142f --- /dev/null +++ b/lib/api/v3/priorities/priority_collection_representer.rb @@ -0,0 +1,57 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'representable/json/collection' +require 'roar/representer/json/hal' + +module API + module V3 + module Priorities + class PriorityCollectionRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + link :self do + "#{root_path}api/v3/priorities" + end + + property :_type, exec_context: :decorator + + collection :priorities, embedded: true, extend: PriorityRepresenter, getter: ->(_) { self } + + def _type + 'Priorities' + end + end + end + end +end diff --git a/lib/api/v3/priorities/priority_model.rb b/lib/api/v3/priorities/priority_model.rb new file mode 100644 index 0000000000..d8410b5c61 --- /dev/null +++ b/lib/api/v3/priorities/priority_model.rb @@ -0,0 +1,43 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'reform' +require 'reform/form/coercion' + +module API + module V3 + module Priorities + class PriorityModel < Reform::Form + include Coercion + + property :name, type: String + end + end + end +end diff --git a/lib/api/v3/priorities/priority_representer.rb b/lib/api/v3/priorities/priority_representer.rb new file mode 100644 index 0000000000..ca3d007050 --- /dev/null +++ b/lib/api/v3/priorities/priority_representer.rb @@ -0,0 +1,54 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'roar/representer/json/hal' + +module API + module V3 + module Priorities + class PriorityRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + property :_type, exec_context: :decorator + + property :id, getter: -> (*) { model.id }, render_nil: true + property :name + + def _type + 'Priority' + end + end + end + end +end diff --git a/lib/api/v3/projects/project_model.rb b/lib/api/v3/projects/project_model.rb new file mode 100644 index 0000000000..756ec1d109 --- /dev/null +++ b/lib/api/v3/projects/project_model.rb @@ -0,0 +1,53 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'reform' +require 'reform/form/coercion' + +module API + module V3 + module Projects + class ProjectModel < Reform::Form + include Coercion + + property :identifier, type: String, virtual: true + property :name, type: String + property :description, type: String + property :homepage, type: String + + property :created_on, type: DateTime, virtual: true + property :updated_on, type: DateTime, virtual: true + + def type + model.project_type.name if model.project_type + end + end + end + end +end diff --git a/lib/api/v3/projects/project_representer.rb b/lib/api/v3/projects/project_representer.rb new file mode 100644 index 0000000000..698218de76 --- /dev/null +++ b/lib/api/v3/projects/project_representer.rb @@ -0,0 +1,74 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'roar/representer/json/hal' + +module API + module V3 + module Projects + class ProjectRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + property :_type, exec_context: :decorator + + link :self do + { + href: "#{root_path}api/v3/projects/#{represented.model.id}", + title: "#{represented.name}" + } + end + + link 'versions' do + "#{root_path}api/v3/projects/#{represented.model.id}/versions" + end + + property :id, getter: -> (*) { model.id }, render_nil: true + property :identifier, render_nil: true + + property :name, render_nil: true + property :description, render_nil: true + property :homepage + + property :created_on, render_nil: true + property :updated_on, render_nil: true + + property :type, render_nil: true + + def _type + 'Project' + end + end + end + end +end diff --git a/lib/api/v3/projects/projects_api.rb b/lib/api/v3/projects/projects_api.rb new file mode 100644 index 0000000000..f645c2cb74 --- /dev/null +++ b/lib/api/v3/projects/projects_api.rb @@ -0,0 +1,57 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +module API + module V3 + module Projects + class ProjectsAPI < Grape::API + + resources :projects do + params do + requires :id, desc: 'Project id' + end + + namespace ':id' do + before do + @project = Project.find(params[:id]) + @model = ProjectModel.new(@project) + end + + get do + authorize(:view_project, context: @project) + ProjectRepresenter.new(@model) + end + + mount API::V3::Versions::VersionsAPI + end + + end + end + end + end +end diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index 0a4b9e260d..a9b5be6314 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -40,7 +40,7 @@ module API before do @query = Query.find(params[:id]) - model = ::API::V3::Queries::QueryModel.new(query: @query) + model = ::API::V3::Queries::QueryModel.new(@query) @representer = ::API::V3::Queries::QueryRepresenter.new(model) end @@ -59,16 +59,16 @@ module API normalized_query_name, @query.id, title: @query.name ) query_menu_item.save! - @representer.to_json + @representer end patch :unstar do authorize({ controller: :queries, action: :unstar }, context: @query.project, allow: allowed_to_manage_stars?) query_menu_item = @query.query_menu_item - return @representer.to_json if @query.query_menu_item.nil? + return @representer if @query.query_menu_item.nil? query_menu_item.destroy @query.reload - @representer.to_json + @representer end end diff --git a/lib/api/v3/queries/query_model.rb b/lib/api/v3/queries/query_model.rb index 3f50e3584f..c10ec0fc01 100644 --- a/lib/api/v3/queries/query_model.rb +++ b/lib/api/v3/queries/query_model.rb @@ -34,24 +34,17 @@ module API module V3 module Queries class QueryModel < Reform::Form - include Composition include Coercion - model :query - - property :name, on: :query, type: String - property :project_id, on: :query, type: Integer - property :user_id, on: :query, type: Integer - property :filters, on: :query, type: String - property :is_public, on: :query, type: String - property :column_names, on: :query, type: String - property :sort_criteria, on: :query, type: String - property :group_by, on: :query, type: String - property :display_sums, on: :query, type: String - - def query - model[:query] - end + property :name, type: String + property :project_id, type: Integer + property :user_id, type: Integer + property :filters, type: String + property :is_public, type: String + property :column_names, type: String + property :sort_criteria, type: String + property :group_by, type: String + property :display_sums, type: String end end end diff --git a/lib/api/v3/queries/query_representer.rb b/lib/api/v3/queries/query_representer.rb index 3900f1111f..2ead890689 100644 --- a/lib/api/v3/queries/query_representer.rb +++ b/lib/api/v3/queries/query_representer.rb @@ -43,23 +43,23 @@ module API property :_type, exec_context: :decorator link :self do - { href: "#{root_url}api/v3/queries/#{represented.query.id}", title: "#{represented.name}" } + { href: "#{root_path}api/v3/queries/#{represented.model.id}", title: "#{represented.name}" } end - property :id, getter: -> (*) { query.id }, render_nil: true + property :id, getter: -> (*) { model.id }, render_nil: true property :name, render_nil: true - property :project_id, getter: -> (*) { query.project.id } - property :project_name, getter: -> (*) { query.project.try(:name) } - property :user_id, getter: -> (*) { query.user.try(:id) }, render_nil: true - property :user_name, getter: -> (*) { query.user.try(:name) }, render_nil: true - property :user_login, getter: -> (*) { query.user.try(:login) }, render_nil: true - property :user_mail, getter: -> (*) { query.user.try(:mail) }, render_nil: true + property :project_id, getter: -> (*) { model.project.id } + property :project_name, getter: -> (*) { model.project.try(:name) } + property :user_id, getter: -> (*) { model.user.try(:id) }, render_nil: true + property :user_name, getter: -> (*) { model.user.try(:name) }, render_nil: true + property :user_login, getter: -> (*) { model.user.try(:login) }, render_nil: true + property :user_mail, getter: -> (*) { model.user.try(:mail) }, render_nil: true property :filters, render_nil: true - property :is_public, getter: -> (*) { query.is_public.to_s }, render_nil: true + property :is_public, getter: -> (*) { model.is_public.to_s }, render_nil: true property :column_names, render_nil: true property :sort_criteria, render_nil: true property :group_by, render_nil: true - property :display_sums, getter: -> (*) { query.display_sums.to_s }, render_nil: true + property :display_sums, getter: -> (*) { model.display_sums.to_s }, render_nil: true property :is_starred, getter: -> (*) { is_starred.to_s }, exec_context: :decorator def _type @@ -67,7 +67,7 @@ module API end def is_starred - return true if !represented.query.query_menu_item.nil? + return true if !represented.model.query_menu_item.nil? false end end diff --git a/lib/api/v3/relations/relations_api.rb b/lib/api/v3/relations/relations_api.rb index c708b0057d..728042d156 100644 --- a/lib/api/v3/relations/relations_api.rb +++ b/lib/api/v3/relations/relations_api.rb @@ -19,11 +19,10 @@ module API r.delay = declared_params[:delay_id] end - if relation.valid? + if relation.valid? && relation.save model = ::API::V3::WorkPackages::RelationModel.new(relation) representer = ::API::V3::WorkPackages::RelationRepresenter.new(model, work_package: relation.to) - relation.save! - representer.to_json + representer else fail Errors::Validation.new(relation) end @@ -32,8 +31,7 @@ module API namespace ':relation_id' do delete do authorize(:manage_work_package_relations, context: @work_package.project) - relation = Relation.find(params[:relation_id]) - relation.delete + Relation.destroy(params[:relation_id]) status 204 end end diff --git a/lib/api/v3/root.rb b/lib/api/v3/root.rb index 49966bff99..99c99bc412 100644 --- a/lib/api/v3/root.rb +++ b/lib/api/v3/root.rb @@ -38,9 +38,16 @@ module API mount ::API::V3::Activities::ActivitiesAPI mount ::API::V3::Attachments::AttachmentsAPI + mount ::API::V3::Priorities::PrioritiesAPI + mount ::API::V3::Projects::ProjectsAPI mount ::API::V3::Queries::QueriesAPI + mount ::API::V3::Statuses::StatusesAPI mount ::API::V3::Users::UsersAPI mount ::API::V3::WorkPackages::WorkPackagesAPI + + get '/' do + RootRepresenter.new({}) + end end end end diff --git a/lib/api/v3/root_representer.rb b/lib/api/v3/root_representer.rb new file mode 100644 index 0000000000..eec3f2fb59 --- /dev/null +++ b/lib/api/v3/root_representer.rb @@ -0,0 +1,58 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'roar/representer/json/hal' + +module API + module V3 + class RootRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = ::API::Utilities::CamelCasingStrategy.new + + link 'priorities' do + "#{root_path}api/v3/priorities" + end + + link 'project' do + { + href: "#{root_path}api/v3/project/{project_id}", + templated: true + } + end + + link 'statuses' do + "#{root_path}api/v3/statuses" + end + end + end +end diff --git a/lib/api/v3/statuses/status_collection_representer.rb b/lib/api/v3/statuses/status_collection_representer.rb new file mode 100644 index 0000000000..7cc5ef7cc4 --- /dev/null +++ b/lib/api/v3/statuses/status_collection_representer.rb @@ -0,0 +1,57 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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 status 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 status 2 +# of the License, or (at your option) any later status. +# +# 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. +#++ + +require 'roar/decorator' +require 'representable/json/collection' +require 'roar/representer/json/hal' + +module API + module V3 + module Statuses + class StatusCollectionRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + link :self do + "#{root_path}api/v3/statuses" + end + + property :_type, exec_context: :decorator + + collection :statuses, embedded: true, extend: StatusRepresenter, getter: ->(_) { self } + + def _type + 'Statuses' + end + end + end + end +end diff --git a/lib/api/v3/statuses/status_model.rb b/lib/api/v3/statuses/status_model.rb new file mode 100644 index 0000000000..94197e1d97 --- /dev/null +++ b/lib/api/v3/statuses/status_model.rb @@ -0,0 +1,43 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'reform' +require 'reform/form/coercion' + +module API + module V3 + module Statuses + class StatusModel < Reform::Form + include Coercion + + property :name, type: String + end + end + end +end diff --git a/lib/api/v3/statuses/status_representer.rb b/lib/api/v3/statuses/status_representer.rb new file mode 100644 index 0000000000..dd1fe4529a --- /dev/null +++ b/lib/api/v3/statuses/status_representer.rb @@ -0,0 +1,54 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'roar/representer/json/hal' + +module API + module V3 + module Statuses + class StatusRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + property :_type, exec_context: :decorator + + property :id, getter: -> (*) { model.id }, render_nil: true + property :name + + def _type + 'Status' + end + end + end + end +end diff --git a/lib/api/v3/statuses/statuses_api.rb b/lib/api/v3/statuses/statuses_api.rb new file mode 100644 index 0000000000..12d1749150 --- /dev/null +++ b/lib/api/v3/statuses/statuses_api.rb @@ -0,0 +1,49 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +module API + module V3 + module Statuses + class StatusesAPI < Grape::API + + resources :statuses do + before do + @statuses = Status.all + @statuses.map! { |status| StatusModel.new(status) } + end + + get do + StatusCollectionRepresenter.new(@statuses) + end + end + + end + end + end +end diff --git a/lib/api/v3/users/user_representer.rb b/lib/api/v3/users/user_representer.rb index 698f9cb62d..cfb5cccee4 100644 --- a/lib/api/v3/users/user_representer.rb +++ b/lib/api/v3/users/user_representer.rb @@ -52,12 +52,12 @@ module API property :_type, exec_context: :decorator link :self do - { href: "#{root_url}api/v3/users/#{represented.model.id}", title: "#{represented.model.name} - #{represented.model.login}" } + { href: "#{root_path}api/v3/users/#{represented.model.id}", title: "#{represented.model.name} - #{represented.model.login}" } end link :removeWatcher do { - href: "#{root_url}/api/v3/work_packages/#{@work_package.id}/watchers/#{represented.model.id}", + href: "#{root_path}api/v3/work_packages/#{@work_package.id}/watchers/#{represented.model.id}", method: :delete, title: 'Remove watcher' } if @work_package && current_user_allowed_to(:delete_work_package_watchers, @work_package) diff --git a/lib/api/v3/users/users_api.rb b/lib/api/v3/users/users_api.rb index 47c625f056..bec83d11db 100644 --- a/lib/api/v3/users/users_api.rb +++ b/lib/api/v3/users/users_api.rb @@ -39,13 +39,12 @@ module API namespace ':id' do before do - @user = User.find(params[:id]) - model = ::API::V3::Users::UserModel.new(@user) - @representer = ::API::V3::Users::UserRepresenter.new(model) + @user = User.find(params[:id]) + @model = UserModel.new(@user) end get do - @representer.to_json + UserRepresenter.new(@model) end end diff --git a/lib/api/v3/versions/version_collection_representer.rb b/lib/api/v3/versions/version_collection_representer.rb new file mode 100644 index 0000000000..ba72192fc9 --- /dev/null +++ b/lib/api/v3/versions/version_collection_representer.rb @@ -0,0 +1,64 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'representable/json/collection' +require 'roar/representer/json/hal' + +module API + module V3 + module Versions + class VersionCollectionRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + attr_reader :project + + def initialize(model, project:) + @project = project + super(model) + end + + link :self do + "#{root_path}api/v3/projects/#{project.id}/versions" + end + + property :_type, exec_context: :decorator + + collection :versions, embedded: true, extend: VersionRepresenter, getter: ->(_) { self } + + def _type + 'Versions' + end + end + end + end +end diff --git a/lib/api/v3/versions/version_model.rb b/lib/api/v3/versions/version_model.rb new file mode 100644 index 0000000000..9d7bc1b63a --- /dev/null +++ b/lib/api/v3/versions/version_model.rb @@ -0,0 +1,43 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'reform' +require 'reform/form/coercion' + +module API + module V3 + module Versions + class VersionModel < Reform::Form + include Coercion + + property :name, type: String + end + end + end +end diff --git a/lib/api/v3/versions/version_representer.rb b/lib/api/v3/versions/version_representer.rb new file mode 100644 index 0000000000..2dc22bfbb9 --- /dev/null +++ b/lib/api/v3/versions/version_representer.rb @@ -0,0 +1,54 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'roar/representer/json/hal' + +module API + module V3 + module Versions + class VersionRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include OpenProject::StaticRouting::UrlHelpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + property :_type, exec_context: :decorator + + property :id, getter: -> (*) { model.id }, render_nil: true + property :name + + def _type + 'Version' + end + end + end + end +end diff --git a/lib/api/v3/versions/versions_api.rb b/lib/api/v3/versions/versions_api.rb new file mode 100644 index 0000000000..321bdcc6fe --- /dev/null +++ b/lib/api/v3/versions/versions_api.rb @@ -0,0 +1,49 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +module API + module V3 + module Versions + class VersionsAPI < Grape::API + + resources :versions do + before do + @versions = @project.shared_versions.all + @versions.map! { |version| VersionModel.new(version) } + end + + get do + VersionCollectionRepresenter.new(@versions, project: @project) + end + end + + end + end + end +end diff --git a/lib/api/v3/work_packages/available_status_collection_representer.rb b/lib/api/v3/work_packages/available_status_collection_representer.rb new file mode 100644 index 0000000000..e0c838b4b5 --- /dev/null +++ b/lib/api/v3/work_packages/available_status_collection_representer.rb @@ -0,0 +1,50 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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 status 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 status 2 +# of the License, or (at your option) any later status. +# +# 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. +#++ + +module API + module V3 + module WorkPackages + class AvailableStatusCollectionRepresenter < ::API::V3::Statuses::StatusCollectionRepresenter + link :self do |opts| + "#{work_package_url(opts[:work_package_id])}/available_statuses" + end + + link :work_package do |opts| + work_package_url(opts[:work_package_id]) + end + + private + + def work_package_url(work_package_id) + "#{root_path}api/v3/work_packages/#{work_package_id}" + end + end + end + end +end diff --git a/lib/api/v3/work_packages/relation_representer.rb b/lib/api/v3/work_packages/relation_representer.rb index d3252752f9..0047fe4d36 100644 --- a/lib/api/v3/work_packages/relation_representer.rb +++ b/lib/api/v3/work_packages/relation_representer.rb @@ -51,20 +51,20 @@ module API property :_type, exec_context: :decorator link :self do - { href: "#{root_url}api/v3/relations/#{represented.model.id}" } + { href: "#{root_path}api/v3/relations/#{represented.model.id}" } end link :relatedFrom do - { href: "#{root_url}api/v3/work_packages/#{represented.model.from_id}" } + { href: "#{root_path}api/v3/work_packages/#{represented.model.from_id}" } end link :relatedTo do - { href: "#{root_url}api/v3/work_packages/#{represented.model.to_id}" } + { href: "#{root_path}api/v3/work_packages/#{represented.model.to_id}" } end link :remove do { - href: "#{root_url}api/v3/work_packages/#{represented.model.from.id}/relations/#{represented.model.id}", + href: "#{root_path}api/v3/work_packages/#{represented.model.from.id}/relations/#{represented.model.id}", method: :delete, title: "Remove relation" } if current_user_allowed_to(:manage_work_package_relations) diff --git a/lib/api/v3/work_packages/statuses_api.rb b/lib/api/v3/work_packages/statuses_api.rb new file mode 100644 index 0000000000..096e34db41 --- /dev/null +++ b/lib/api/v3/work_packages/statuses_api.rb @@ -0,0 +1,67 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +module API + module V3 + module WorkPackages + class StatusesAPI < Grape::API + class AvailableStatusesFormatter + # this is an ugly hack to get the work package id for the path to self + def work_package_id(env) + env['rack.routing_args'][:id] + end + + def call(object, env) + if object.respond_to?(:to_json) + object.to_json(work_package_id: work_package_id(env)) + else + MultiJson.dump(object) + end + end + end + + formatter 'hal+json', AvailableStatusesFormatter.new + + get '/available_statuses' do + authorize({ controller: :work_packages, action: :update }, context: work_package.project) + + work_package.type = work_package.project.types.find_by_name(params[:type]) if params[:type] + + statuses = work_package.new_statuses_allowed_to(current_user) + + models = statuses.map { |status| ::API::V3::Statuses::StatusModel.new(status) } + represented = ::API::V3::WorkPackages::AvailableStatusCollectionRepresenter.new(models) + + represented + end + end + end + end +end + diff --git a/lib/api/v3/work_packages/work_package_model.rb b/lib/api/v3/work_packages/work_package_model.rb index 9cb8061fa5..5c62ad0677 100644 --- a/lib/api/v3/work_packages/work_package_model.rb +++ b/lib/api/v3/work_packages/work_package_model.rb @@ -34,7 +34,6 @@ module API module V3 module WorkPackages class WorkPackageModel < Reform::Form - include Composition include Coercion include ActionView::Helpers::UrlHelper include OpenProject::TextFormatting @@ -44,122 +43,125 @@ module API # N.B. required by ActionView::Helpers::UrlHelper def controller; nil; end - model :work_package + property :subject, type: String + property :start_date, type: Date + property :due_date, type: Date + property :created_at, type: DateTime + property :updated_at, type: DateTime + property :author, type: String + property :project_id, type: Integer + property :parent_id, type: Integer + property :responsible_id, type: Integer + property :assigned_to_id, type: Integer + property :fixed_version_id, type: Integer - property :subject, on: :work_package, type: String - property :start_date, on: :work_package, type: Date - property :due_date, on: :work_package, type: Date - property :created_at, on: :work_package, type: DateTime - property :updated_at, on: :work_package, type: DateTime - property :author, on: :work_package, type: String - property :project_id, on: :work_package, type: Integer - property :responsible_id, on: :work_package, type: Integer - property :assigned_to_id, on: :work_package, type: Integer - property :fixed_version_id, on: :work_package, type: Integer - - def work_package - model[:work_package] - end def description - format_text(work_package, :description) + format_text(model, :description) end def raw_description - work_package.description + model.description end def raw_description=(value) - work_package.description = value + model.description = value end def type - work_package.type.try(:name) + model.type.try(:name) end def type=(value) - type = Type.find(:first, conditions: ['name ilike ?', value]) - work_package.type = type + model.type = Type.find_by_name(value) end def status - work_package.status.try(:name) + model.status.try(:name) end def status=(value) - status = Status.find(:first, conditions: ['name ilike ?', value]) - work_package.status = status + model.status = Status.find_by_name(value) end def priority - work_package.priority.try(:name) + model.priority.try(:name) end def priority=(value) - priority = IssuePriority.find(:first, conditions: ['name ilike ?', value]) - work_package.priority = priority + model.priority = IssuePriority.find_by_name(value) end def estimated_time - { units: 'hours', value: work_package.estimated_hours } + { units: I18n.t(:'datetime.units.hour', count: model.estimated_hours.to_i), + value: model.estimated_hours } end def estimated_time=(value) hours = ActiveSupport::JSON.decode(value)['value'] - work_package.estimated_hours = hours + model.estimated_hours = hours end def version_id=(value) - work_package.fixed_version_id = value + model.fixed_version_id = value end def percentage_done - work_package.done_ratio + model.done_ratio end def percentage_done=(value) - work_package.done_ratio = value + model.done_ratio = value end def author - ::API::V3::Users::UserModel.new(work_package.author) unless work_package.author.nil? + ::API::V3::Users::UserModel.new(model.author) unless model.author.nil? end def responsible - ::API::V3::Users::UserModel.new(work_package.responsible) unless work_package.responsible.nil? + ::API::V3::Users::UserModel.new(model.responsible) unless model.responsible.nil? end def assignee - ::API::V3::Users::UserModel.new(work_package.assigned_to) unless work_package.assigned_to.nil? + ::API::V3::Users::UserModel.new(model.assigned_to) unless model.assigned_to.nil? end def activities - work_package.journals.map{ |journal| ::API::V3::Activities::ActivityModel.new(journal) } + model.journals.map{ |journal| ::API::V3::Activities::ActivityModel.new(journal) } end def attachments - work_package.attachments + model.attachments .map{ |attachment| ::API::V3::Attachments::AttachmentModel.new(attachment) } end def watchers - work_package.watcher_users + model.watcher_users .order(User::USER_FORMATS_STRUCTURE[Setting.user_format]) .map{ |u| ::API::V3::Users::UserModel.new(u) } end def relations - relations = work_package.relations - visible_relations = relations.find_all { |relation| relation.other_work_package(work_package).visible? } + relations = model.relations + visible_relations = relations.find_all { |relation| relation.other_work_package(model).visible? } visible_relations.map{ |relation| RelationModel.new(relation) } end def is_closed - work_package.closed? + model.closed? end validates_presence_of :subject, :project_id, :type, :author, :status validates_length_of :subject, maximum: 255 + validate :validate_parent_constraint + + private + + def validate_parent_constraint + if model.parent + errors.add :parent_id, :cannot_be_milestone if model.parent.is_milestone? + end + end end end end diff --git a/lib/api/v3/work_packages/work_package_representer.rb b/lib/api/v3/work_packages/work_package_representer.rb index f91d02e1ae..6d24aec34a 100644 --- a/lib/api/v3/work_packages/work_package_representer.rb +++ b/lib/api/v3/work_packages/work_package_representer.rb @@ -50,95 +50,114 @@ module API property :_type, exec_context: :decorator link :self do - { href: "#{root_url}api/v3/work_packages/#{represented.work_package.id}", title: represented.subject } + { + href: "#{root_path}api/v3/work_packages/#{represented.model.id}", + title: "#{represented.subject}" + } + end + + link :update do + { + href: "#{root_path}api/v3/work_packages/#{represented.model.id}", + method: :patch, + title: "Update #{represented.subject}" + } if current_user_allowed_to(:edit_work_packages, represented.model) end link :author do { - href: "#{root_url}/api/v3/users/#{represented.work_package.author.id}", - title: "#{represented.work_package.author.name} - #{represented.work_package.author.login}" - } unless represented.work_package.author.nil? + href: "#{root_path}api/v3/users/#{represented.model.author.id}", + title: "#{represented.model.author.name} - #{represented.model.author.login}" + } unless represented.model.author.nil? end link :responsible do { - href: "#{root_url}/api/v3/users/#{represented.work_package.responsible.id}", - title: "#{represented.work_package.responsible.name} - #{represented.work_package.responsible.login}" - } unless represented.work_package.responsible.nil? + href: "#{root_path}api/v3/users/#{represented.model.responsible.id}", + title: "#{represented.model.responsible.name} - #{represented.model.responsible.login}" + } unless represented.model.responsible.nil? end link :assignee do { - href: "#{root_url}/api/v3/users/#{represented.work_package.assigned_to.id}", - title: "#{represented.work_package.assigned_to.name} - #{represented.work_package.assigned_to.login}" - } unless represented.work_package.assigned_to.nil? + href: "#{root_path}api/v3/users/#{represented.model.assigned_to.id}", + title: "#{represented.model.assigned_to.name} - #{represented.model.assigned_to.login}" + } unless represented.model.assigned_to.nil? + end + + link :availableStatuses do + { + href: "#{root_path}api/v3/work_packages/#{represented.model.id}/available_statuses", + title: 'Available Statuses' + } if @current_user.allowed_to?({ controller: :work_packages, action: :update }, + represented.model.project) end link :availableWatchers do - { - href: "#{root_url}api/v3/work_packages/#{represented.work_package.id}/available_watchers", - title: "Available Watchers" - } + { + href: "#{root_path}api/v3/work_packages/#{represented.model.id}/available_watchers", + title: 'Available Watchers' + } end link :watch do { - href: "#{root_url}/api/v3/work_packages/#{represented.work_package.id}/watchers", - method: :post, - data: { user_id: @current_user.id }, - title: 'Watch work package' + href: "#{root_path}api/v3/work_packages/#{represented.model.id}/watchers", + method: :post, + data: { user_id: @current_user.id }, + title: 'Watch work package' } if !@current_user.anonymous? && - current_user_allowed_to(:view_work_packages, represented.work_package) && - !represented.work_package.watcher_users.include?(@current_user) + current_user_allowed_to(:view_work_packages, represented.model) && + !represented.model.watcher_users.include?(@current_user) end link :unwatch do { - href: "#{root_url}/api/v3/work_packages/#{represented.work_package.id}/watchers/#{@current_user.id}", - method: :delete, - title: 'Unwatch work package' - } if current_user_allowed_to(:view_work_packages, represented.work_package) && represented.work_package.watcher_users.include?(@current_user) + href: "#{root_path}api/v3/work_packages/#{represented.model.id}/watchers/#{@current_user.id}", + method: :delete, + title: 'Unwatch work package' + } if current_user_allowed_to(:view_work_packages, represented.model) && represented.model.watcher_users.include?(@current_user) end link :addWatcher do { - href: "#{root_url}/api/v3/work_packages/#{represented.work_package.id}/watchers{?user_id}", - method: :post, - title: 'Add watcher', - templated: true - } if current_user_allowed_to(:add_work_package_watchers, represented.work_package) + href: "#{root_path}api/v3/work_packages/#{represented.model.id}/watchers{?user_id}", + method: :post, + title: 'Add watcher', + templated: true + } if current_user_allowed_to(:add_work_package_watchers, represented.model) end link :addRelation do { - href: "#{root_url}/api/v3/work_packages/#{represented.work_package.id}/relations", + href: "#{root_path}api/v3/work_packages/#{represented.model.id}/relations", method: :post, title: 'Add relation' - } if current_user_allowed_to(:manage_work_package_relations, represented.work_package) + } if current_user_allowed_to(:manage_work_package_relations, represented.model) end link :addComment do { - href: "#{root_url}api/v3/work_packages/#{represented.work_package.id}/activities", + href: "#{root_path}api/v3/work_packages/#{represented.model.id}/activities", method: :post, title: 'Add comment' - } if current_user_allowed_to(:add_work_package_notes, represented.work_package) + } if current_user_allowed_to(:add_work_package_notes, represented.model) end link :parent do { - href: "#{root_url}/api/v3/work_packages/#{represented.work_package.parent.id}", - title: represented.work_package.parent.subject - } unless represented.work_package.parent.nil? || !represented.work_package.parent.visible? + href: "#{root_path}api/v3/work_packages/#{represented.model.parent.id}", + title: represented.model.parent.subject + } unless represented.model.parent.nil? || !represented.model.parent.visible? end links :children do visible_children.map do |child| - { href: "#{root_url}/api/v3/work_packages/#{child.id}", title: child.subject } + { href: "#{root_path}api/v3/work_packages/#{child.id}", title: child.subject } end unless visible_children.empty? end - property :id, getter: -> (*) { work_package.id }, render_nil: true + property :id, getter: -> (*) { model.id }, render_nil: true property :subject, render_nil: true property :type, render_nil: true property :description, render_nil: true @@ -146,16 +165,17 @@ module API property :status, render_nil: true property :is_closed property :priority, render_nil: true - property :start_date, getter: -> (*) { work_package.start_date }, render_nil: true - property :due_date, getter: -> (*) { work_package.due_date }, render_nil: true + property :start_date, getter: -> (*) { model.start_date.to_datetime.utc.iso8601 unless model.start_date.nil? }, render_nil: true + property :due_date, getter: -> (*) { model.due_date.to_datetime.utc.iso8601 unless model.due_date.nil? }, render_nil: true property :estimated_time, render_nil: true property :percentage_done, render_nil: true - property :version_id, getter: -> (*) { work_package.fixed_version.try(:id) }, render_nil: true - property :version_name, getter: -> (*) { work_package.fixed_version.try(:name) }, render_nil: true - property :project_id, getter: -> (*) { work_package.project.id } - property :project_name, getter: -> (*) { work_package.project.try(:name) } - property :created_at, getter: -> (*) { work_package.created_at.utc.iso8601}, render_nil: true - property :updated_at, getter: -> (*) { work_package.updated_at.utc.iso8601}, render_nil: true + property :version_id, getter: -> (*) { model.fixed_version.try(:id) }, render_nil: true + property :version_name, getter: -> (*) { model.fixed_version.try(:name) }, render_nil: true + property :project_id, getter: -> (*) { model.project.id } + property :project_name, getter: -> (*) { model.project.try(:name) } + property :parent_id, render_nil: true + property :created_at, getter: -> (*) { model.created_at.utc.iso8601}, render_nil: true + property :updated_at, getter: -> (*) { model.updated_at.utc.iso8601}, render_nil: true collection :custom_properties, exec_context: :decorator, render_nil: true @@ -164,7 +184,7 @@ module API property :assignee, embedded: true, class: ::API::V3::Users::UserModel, decorator: ::API::V3::Users::UserRepresenter, if: -> (*) { !assignee.nil? } property :activities, embedded: true, exec_context: :decorator - property :watchers, embedded: true, exec_context: :decorator, if: -> (*) { current_user_allowed_to(:view_work_package_watchers, represented.work_package) } + property :watchers, embedded: true, exec_context: :decorator, if: -> (*) { current_user_allowed_to(:view_work_package_watchers, represented.model) } collection :attachments, embedded: true, class: ::API::V3::Attachments::AttachmentModel, decorator: ::API::V3::Attachments::AttachmentRepresenter property :relations, embedded: true, exec_context: :decorator @@ -177,24 +197,24 @@ module API end def watchers - represented.watchers.map{ |watcher| ::API::V3::Users::UserRepresenter.new(watcher, work_package: represented.work_package, current_user: @current_user) } + represented.watchers.map{ |watcher| ::API::V3::Users::UserRepresenter.new(watcher, work_package: represented.model, current_user: @current_user) } end def relations - represented.relations.map{ |relation| RelationRepresenter.new(relation, work_package: represented.work_package, current_user: @current_user) } + represented.relations.map{ |relation| RelationRepresenter.new(relation, work_package: represented.model, current_user: @current_user) } end def custom_properties - values = represented.work_package.custom_field_values + values = represented.model.custom_field_values values.map { |v| { name: v.custom_field.name, format: v.custom_field.field_format, value: v.value }} end def current_user_allowed_to(permission, work_package) - @current_user && @current_user.allowed_to?(permission, work_package.project) + @current_user && @current_user.allowed_to?(permission, represented.model.project) end def visible_children - @visible_children ||= represented.work_package.children.find_all { |child| child.visible? } + @visible_children ||= represented.model.children.find_all { |child| child.visible? } end end end diff --git a/lib/api/v3/work_packages/work_packages_api.rb b/lib/api/v3/work_packages/work_packages_api.rb index 7d0b18539f..0dc08ac5c5 100644 --- a/lib/api/v3/work_packages/work_packages_api.rb +++ b/lib/api/v3/work_packages/work_packages_api.rb @@ -38,15 +38,30 @@ module API end namespace ':id' do + helpers do + attr_reader :work_package + end + before do @work_package = WorkPackage.find(params[:id]) - model = ::API::V3::WorkPackages::WorkPackageModel.new(work_package: @work_package) + model = ::API::V3::WorkPackages::WorkPackageModel.new(@work_package) @representer = ::API::V3::WorkPackages::WorkPackageRepresenter.new(model, { current_user: current_user }, :activities, :users) end get do authorize({ controller: :work_packages_api, action: :get }, context: @work_package.project) - @representer.to_json + @representer + end + + patch do + authorize(:edit_work_packages, context: @work_package.project) + @representer.from_json(env['api.request.input']) + @representer.represented.sync + if @representer.represented.model.valid? && @representer.represented.save + @representer + else + fail Errors::Validation.new(@representer.represented.model) + end end resource :activities do @@ -57,7 +72,7 @@ module API model = ::API::V3::Activities::ActivityModel.new(work_package.journals.last) representer = ::API::V3::Activities::ActivityRepresenter.new(model, { current_user: current_user }) - representer.to_json + representer else errors = work_package.errors.full_messages.join(", ") fail Errors::Validation.new(work_package, description: errors) @@ -94,7 +109,9 @@ module API end mount ::API::V3::WorkPackages::WatchersAPI + mount ::API::V3::WorkPackages::StatusesAPI mount ::API::V3::Relations::RelationsAPI + end end diff --git a/lib/plugins/acts_as_journalized/lib/journal_formatter/base.rb b/lib/plugins/acts_as_journalized/lib/journal_formatter/base.rb index c778b18536..8abe5c6e07 100644 --- a/lib/plugins/acts_as_journalized/lib/journal_formatter/base.rb +++ b/lib/plugins/acts_as_journalized/lib/journal_formatter/base.rb @@ -64,9 +64,9 @@ class JournalFormatter::Base def format_html_details(label, old_value, value) label = content_tag('strong', label) - old_value = content_tag("i", h(old_value)) if old_value && !old_value.blank? + old_value = content_tag("i", h(old_value), title: h(old_value)) if old_value && !old_value.blank? old_value = content_tag("strike", old_value) if old_value and value.blank? - value = content_tag("i", h(value)) if value.present? + value = content_tag("i", h(value), title: h(value)) if value.present? value ||= "" [label, old_value, value] diff --git a/lib/redmine/i18n.rb b/lib/redmine/i18n.rb index e768a52867..eb1aaf546f 100644 --- a/lib/redmine/i18n.rb +++ b/lib/redmine/i18n.rb @@ -101,7 +101,12 @@ module Redmine end def all_languages - @@all_languages ||= Dir.glob(Rails.root.join('config/locales/*.yml')).collect {|f| File.basename(f).split('.').first}.collect(&:to_sym) + @@all_languages ||= begin + Dir.glob(Rails.root.join('config/locales/*.yml')) + .map { |f| File.basename(f).split('.').first } + .reject! { |l| /\Ajs-/.match(l.to_s) } + .map(&:to_sym) + end end def find_language(lang) diff --git a/lib/redmine/wiki_formatting/textile/helper.rb b/lib/redmine/wiki_formatting/textile/helper.rb index 85e4bdefee..be0bd5568c 100644 --- a/lib/redmine/wiki_formatting/textile/helper.rb +++ b/lib/redmine/wiki_formatting/textile/helper.rb @@ -38,7 +38,13 @@ module Redmine :class => 'icon icon-help', :onclick => "window.open(\"#{ url }\", \"\", \"resizable=yes, location=no, width=600, height=640, menubar=no, status=no, scrollbars=yes\"); return false;") - javascript_tag("var wikiToolbar = new jsToolBar($('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript help_link}'); wikiToolbar.draw();") + javascript_tag(<<-EOF) + var wikiToolbar = new jsToolBar($('#{field_id}')); + wikiToolbar.setHelpLink('#{escape_javascript help_link}'); + // initialize the toolbar later, so that i18n-js has a chance to set the translations + // for the wiki-buttons first. + jQuery(function(){ wikiToolbar.draw(); }); + EOF end def initial_page_content(page) @@ -46,12 +52,6 @@ module Redmine end def heads_for_wiki_formatter - unless @heads_for_wiki_formatter_included - content_for :header_tags do - javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language.to_s.downcase}") - end - @heads_for_wiki_formatter_included = true - end end end end diff --git a/lib/tasks/locales.rake b/lib/tasks/locales.rake deleted file mode 100644 index ee50f6751f..0000000000 --- a/lib/tasks/locales.rake +++ /dev/null @@ -1,145 +0,0 @@ -#-- encoding: UTF-8 -#-- copyright -# OpenProject is a project management system. -# Copyright (C) 2012-2014 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. -#++ - -desc 'Updates and checks locales against en.yml' -task :locales do - %w(locales:update locales:check_interpolation).collect do |task| - Rake::Task[task].invoke - end -end - -namespace :locales do - desc 'Updates language files based on en.yml content (only works for new top level keys).' - task :update do - dir = ENV['DIR'] || './config/locales' - - en_strings = YAML.load(File.read(File.join(dir,'en.yml')))['en'] - - files = Dir.glob(File.join(dir,'*.{yaml,yml}')) - files.each do |file| - puts "Updating file #{file}" - file_strings = YAML.load(File.read(file)) - file_strings = file_strings[file_strings.keys.first] - - missing_keys = en_strings.keys - file_strings.keys - next if missing_keys.empty? - - puts "==> Missing #{missing_keys.size} keys (#{missing_keys.join(', ')})" - lang = File.open(file, 'a') - - missing_keys.each do |key| - {key => en_strings[key]}.to_yaml.each_line do |line| - next if line =~ /\A---/ || line.empty? - puts " #{line}" - lang << " #{line}" - end - end - - lang.close - end - end - - desc 'Checks interpolation arguments in locals against en.yml' - task :check_interpolation do - dir = ENV['DIR'] || './config/locales' - en_strings = YAML.load(File.read(File.join(dir,'en.yml')))['en'] - files = Dir.glob(File.join(dir,'*.{yaml,yml}')) - files.each do |file| - file_strings = YAML.load(File.read(file)) - file_strings = file_strings[file_strings.keys.first] - - file_strings.each do |key, string| - next unless string.is_a?(String) - string.scan /%\{\w+\}/ do |match| - unless en_strings[key].nil? || en_strings[key].include?(match) - puts "#{file}: #{key} uses #{match} not found in en.yml" - end - end - end - end - end - - desc <<-END_DESC -Removes a translation string from all locale file (only works for top-level childless non-multiline keys, probably doesn\'t work on windows). - -Options: - key=key_1,key_2 Comma-separated list of keys to delete - skip=en,de Comma-separated list of locale files to ignore (filename without extension) -END_DESC - - task :remove_key do - dir = ENV['DIR'] || './config/locales' - files = Dir.glob(File.join(dir,'*.yml')) - skips = ENV['skip'] ? Regexp.union(ENV['skip'].split(',')) : nil - deletes = ENV['key'] ? Regexp.union(ENV['key'].split(',')) : nil - # Ignore multiline keys (begin with | or >) and keys with children (nothing meaningful after :) - delete_regex = /\A #{deletes}: +[^\|>\s#].*\z/ - - files.each do |path| - # Skip certain locales - (puts "Skipping #{path}"; next) if File.basename(path, ".yml") =~ skips - puts "Deleting selected keys from #{path}" - orig_content = File.open(path, 'r') {|file| file.read} - File.open(path, 'w') {|file| orig_content.each_line {|line| file.puts line unless line.chomp =~ delete_regex}} - end - end - - desc <<-END_DESC -Adds a new top-level translation string to all locale file (only works for childless keys, probably doesn\'t work on windows, doesn't check for duplicates). - -Options: - key="some_key=foo" - key1="another_key=bar" - key_fb="foo=bar" Keys to add in the form key=value, every option of the form key[,\\d,_*] will be recognised - skip=en,de Comma-separated list of locale files to ignore (filename without extension) -END_DESC - - task :add_key do - dir = ENV['DIR'] || './config/locales' - files = Dir.glob(File.join(dir,'*.yml')) - skips = ENV['skip'] ? Regexp.union(ENV['skip'].split(',')) : nil - keys_regex = /\Akey(\d+|_.+)?\z/ - adds = ENV.reject {|k,v| !(k =~ keys_regex)}.values.collect {|v| Array.new v.split("=",2)} - key_list = adds.collect {|v| v[0]}.join(", ") - - files.each do |path| - # Skip certain locales - (puts "Skipping #{path}"; next) if File.basename(path, ".yml") =~ skips - # TODO: Check for dupliate/existing keys - puts "Adding #{key_list} to #{path}" - File.open(path, 'a') do |file| - adds.each do |kv| - Hash[*kv].to_yaml.each_line do |line| - file.puts " #{line}" unless (line =~ /\A---/ || line.empty?) - end - end - end - end - end -end diff --git a/public/templates/work_packages.list.details.html b/public/templates/work_packages.list.details.html index 6d7e2d3d44..66f4280ace 100644 --- a/public/templates/work_packages.list.details.html +++ b/public/templates/work_packages.list.details.html @@ -27,7 +27,7 @@ diff --git a/public/templates/work_packages/tabs/_work_package_relations.html b/public/templates/work_packages/tabs/_work_package_relations.html index e281e674c8..fb375d31bc 100644 --- a/public/templates/work_packages/tabs/_work_package_relations.html +++ b/public/templates/work_packages/tabs/_work_package_relations.html @@ -46,8 +46,8 @@
- - + +
diff --git a/public/templates/work_packages/tabs/overview.html b/public/templates/work_packages/tabs/overview.html index 8fb1b63b74..85ff15bbda 100644 --- a/public/templates/work_packages/tabs/overview.html +++ b/public/templates/work_packages/tabs/overview.html @@ -15,6 +15,10 @@