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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ {{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;