diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index 697ee6005b..fa16fb0716 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -48,6 +48,7 @@ en: button_edit: "Edit" button_filter: "Filter" button_list_view: "List view" + button_show_view: "Fullscreen view" button_log_time: "Log time" button_more: "More" button_move: "Move" diff --git a/frontend/app/routing.js b/frontend/app/routing.js index 7bbc914ef2..d034c763b8 100644 --- a/frontend/app/routing.js +++ b/frontend/app/routing.js @@ -67,6 +67,35 @@ angular.module('openproject') } } }) + + .state('work-packages.show', { + url: '/{workPackageId:[0-9]+}?query_props', + templateUrl: '/templates/work_packages.show.html', + controller: 'WorkPackageShowController', + resolve: { + workPackage: function(WorkPackageService, $stateParams) { + return WorkPackageService.getWorkPackage($stateParams.workPackageId); + } + } + }) + .state('work-packages.show.activity', { + url: '/activity', + templateUrl: '/templates/work_packages/tabs/activity.html' + }) + .state('work-packages.show.activity.details', { + url: '#{activity_no:[0-9]+}', + templateUrl: '/templates/work_packages/tabs/activity.html' + }) + .state('work-packages.show.relations', { + url: '/relations', + templateUrl: '/templates/work_packages/tabs/relations.html' + }) + .state('work-packages.show.watchers', { + url: '/watchers', + controller: 'DetailsTabWatchersController', + templateUrl: '/templates/work_packages/tabs/watchers.html' + }) + .state('work-packages.list', { url: '', controller: 'WorkPackagesListController', @@ -79,7 +108,7 @@ angular.module('openproject') templateUrl: '/templates/work_packages.list.new.html' }) .state('work-packages.list.details', { - url: '/{workPackageId:[0-9]+}?query_props', + url: '/details/{workPackageId:[0-9]+}?query_props', templateUrl: '/templates/work_packages.list.details.html', controller: 'WorkPackageDetailsController', resolve: { diff --git a/frontend/app/templates/work_packages.list.html b/frontend/app/templates/work_packages.list.html index c1e321fa88..b41d516cda 100644 --- a/frontend/app/templates/work_packages.list.html +++ b/frontend/app/templates/work_packages.list.html @@ -62,6 +62,19 @@ +
  • + + +
  • diff --git a/frontend/app/templates/work_packages.show.html b/frontend/app/templates/work_packages.show.html new file mode 100644 index 0000000000..b01ae7cf38 --- /dev/null +++ b/frontend/app/templates/work_packages.show.html @@ -0,0 +1,166 @@ +
    + +
    + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    +
    +

    + {{ I18n.t('js.label_description') }} +

    +
    +
    + +
    + +
    +
    + +
    + +
    +
    +

    +
    +
    + + +
    +
    + +
    +
    + {{vm.getLabel(vm.workPackage, field)}} + * +
    +
    + +
    +
    +
    + + + +
    +
    +
    diff --git a/frontend/app/work_packages/controllers/index.js b/frontend/app/work_packages/controllers/index.js index a25d621e7e..d1b90d3f86 100644 --- a/frontend/app/work_packages/controllers/index.js +++ b/frontend/app/work_packages/controllers/index.js @@ -109,6 +109,26 @@ angular.module('openproject.workPackages.controllers') 'WorkPackagesDisplayHelper', require('./work-package-new-controller') ]) + .controller('WorkPackageShowController', [ + '$scope', + '$state', + 'latestTab', + 'workPackage', + 'I18n', + 'RELATION_TYPES', + 'RELATION_IDENTIFIERS', + '$q', + 'WorkPackagesHelper', + 'PathHelper', + 'UsersHelper', + 'ConfigurationService', + 'WorkPackageService', + 'CommonRelationsHandler', + 'ChildrenRelationsHandler', + 'ParentRelationsHandler', + 'EditableFieldsState', + require('./work-package-show-controller') + ]) .controller('WorkPackagesController', [ '$scope', '$state', diff --git a/frontend/app/work_packages/controllers/work-package-show-controller.js b/frontend/app/work_packages/controllers/work-package-show-controller.js new file mode 100644 index 0000000000..b8a13789ca --- /dev/null +++ b/frontend/app/work_packages/controllers/work-package-show-controller.js @@ -0,0 +1,206 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See doc/COPYRIGHT.rdoc for more details. +//++ + +module.exports = function($scope, + $state, + latestTab, + workPackage, + I18n, + RELATION_TYPES, + RELATION_IDENTIFIERS, + $q, + WorkPackagesHelper, + PathHelper, + UsersHelper, + ConfigurationService, + WorkPackageService, + CommonRelationsHandler, + ChildrenRelationsHandler, + ParentRelationsHandler + ) { + $scope.$on('$stateChangeSuccess', function(event, toState){ + latestTab.registerState(toState.name); + }); + + $scope.$on('workPackageRefreshRequired', function(e, callback) { + refreshWorkPackage(callback); + }); + + // initialization + setWorkPackageScopeProperties(workPackage); + + $scope.I18n = I18n; + $scope.$parent.preselectedWorkPackageId = $scope.workPackage.props.id; + $scope.maxDescriptionLength = 800; + + function refreshWorkPackage(callback) { + WorkPackageService.getWorkPackage($scope.workPackage.props.id) + .then(function(workPackage) { + setWorkPackageScopeProperties(workPackage); + $scope.$broadcast('workPackageRefreshed'); + if (callback) { + callback(workPackage); + } + }); + } + $scope.refreshWorkPackage = refreshWorkPackage; // expose to child controllers + + // Inform parent that work package is loaded so back url can be maintained + $scope.$emit('workPackgeLoaded'); + + function outputMessage(message, isError) { + $scope.$emit('flashMessage', { + isError: !!isError, + text: message + }); + } + + function outputError(error) { + outputMessage(error.message, true); + } + + $scope.outputMessage = outputMessage; // expose to child controllers + $scope.outputError = outputError; // expose to child controllers + + function setWorkPackageScopeProperties(workPackage){ + $scope.workPackage = workPackage; + $scope.isWatched = !!workPackage.links.unwatch; + + if (workPackage.links.watch === undefined) { + $scope.toggleWatchLink = workPackage.links.unwatch; + } else { + $scope.toggleWatchLink = workPackage.links.watch; + } + + // autocomplete path + var projectId = workPackage.embedded.project.props.id; + $scope.autocompletePath = PathHelper.staticWorkPackagesAutocompletePath(projectId); + + // activities and latest activities + $scope.activitiesSortedInDescendingOrder = ConfigurationService.commentsSortedInDescendingOrder(); + $scope.activities = displayedActivities($scope.workPackage); + + // watchers + if(workPackage.links.watchers) { + $scope.watchers = workPackage.embedded.watchers.embedded.elements; + } + + $scope.showStaticPagePath = PathHelper.staticWorkPackagePath($scope.workPackage.props.id); + + // Type + $scope.type = workPackage.embedded.type; + + // 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.embedded.elements; + + // relations + $q.all(WorkPackagesHelper.getParent(workPackage)).then(function(parents) { + var relationsHandler = new ParentRelationsHandler(workPackage, parents, 'parent'); + $scope.wpParent = relationsHandler; + }); + + $q.all(WorkPackagesHelper.getChildren(workPackage)).then(function(children) { + var relationsHandler = new ChildrenRelationsHandler(workPackage, children); + $scope.wpChildren = relationsHandler; + }); + + function relationTypeIterator(key) { + $q.all(WorkPackagesHelper.getRelationsOfType( + workPackage, + RELATION_TYPES[key]) + ).then(function(relations) { + var relationsHandler = new CommonRelationsHandler(workPackage, + relations, + RELATION_IDENTIFIERS[key]); + $scope[key] = relationsHandler; + }); + } + + for (var key in RELATION_TYPES) { + if (RELATION_TYPES.hasOwnProperty(key)) { + relationTypeIterator(key); + } + } + } + + $scope.toggleWatch = function() { + var fetchOptions = { + method: $scope.toggleWatchLink.props.method + }; + + if($scope.toggleWatchLink.props.payload !== undefined) { + fetchOptions.contentType = 'application/json; charset=utf-8'; + fetchOptions.data = JSON.stringify($scope.toggleWatchLink.props.payload); + } + + $scope.toggleWatchLink + .fetch({ajax: fetchOptions}) + .then(refreshWorkPackage, outputError); + }; + + $scope.canViewWorkPackageWatchers = function() { + return !!($scope.workPackage && $scope.workPackage.embedded.watchers !== undefined); + }; + + function displayedActivities(workPackage) { + var activities = workPackage.embedded.activities; + + if ($scope.activitiesSortedInDescendingOrder) { + activities.reverse(); + } + return activities; + } + + // toggles + + $scope.toggleStates = { + hideFullDescription: true, + hideAllAttributes: true + }; + + function getFocusAnchorLabel(tab, workPackage) { + var tabLabel = I18n.t('js.work_packages.tabs.' + tab), + params = { + tab: tabLabel, + type: workPackage.props.type, + subject: workPackage.props.subject + }; + + return I18n.t('js.label_work_package_details_you_are_here', params); + } + + $scope.focusAnchorLabel = getFocusAnchorLabel( + $state.current.url.replace(/\//, ''), + $scope.workPackage + ); +}; diff --git a/frontend/app/work_packages/controllers/work-packages-controller.js b/frontend/app/work_packages/controllers/work-packages-controller.js index fa0ee57290..d10519d9af 100644 --- a/frontend/app/work_packages/controllers/work-packages-controller.js +++ b/frontend/app/work_packages/controllers/work-packages-controller.js @@ -49,6 +49,10 @@ module.exports = function($scope, $state, $stateParams, QueryService, PathHelper return $state.includes('work-packages.list.details'); }; + $scope.isShowViewActive = function() { + return $state.includes('work-packages.show'); + }; + $scope.getToggleActionLabel = function(active) { return (active) ? I18n.t('js.label_deactivate') : I18n.t('js.label_activate'); }; diff --git a/frontend/app/work_packages/controllers/work-packages-list-controller.js b/frontend/app/work_packages/controllers/work-packages-list-controller.js index 7f33c9c9fa..bc1192cc2a 100644 --- a/frontend/app/work_packages/controllers/work-packages-list-controller.js +++ b/frontend/app/work_packages/controllers/work-packages-list-controller.js @@ -301,6 +301,15 @@ module.exports = function($scope, $rootScope, $state, $location, latestTab, } }; + $scope.showWorkPackageShowView = function() { + var id = $state.params.workPackageId; + if (id) { + $state.go('work-packages.show.activity', {workPackageId: id}); + } else { + $state.go('work-packages.show.activity', {workPackageId: $scope.preselectedWorkPackageId}); + } + }; + $scope.getFilterCount = function() { if ($scope.query) { var filters = $scope.query.filters;