parent
c2439d20ce
commit
3d9b7266b3
@ -0,0 +1,166 @@ |
||||
<div class="toolbar-container"> |
||||
<div toolbar id="toolbar"> |
||||
<h2 title="{{workPackage.props.subject}}"><a href="" ng-bind="type.props.name" /> {{workPackage.props.subject}}</h2> |
||||
|
||||
<ul id="toolbar-items"> |
||||
<li class="toolbar-item"> |
||||
<button class="button -alt-highlight" |
||||
has-dropdown-menu |
||||
target="TasksDropdownMenu" |
||||
locals="availableTypes,projectIdentifier" |
||||
ng-disabled="cannot('work_package', 'create')"> |
||||
<i class="button--icon icon-add"></i> |
||||
<span class="button--text" ng-bind="::I18n.t('js.toolbar.unselected_title')"></span> |
||||
<i class="button--dropdown-indicator"></i> |
||||
</button> |
||||
</li> |
||||
<li class="toolbar-item" feature-flag="detailsView"> |
||||
<ul id="work-packages-view-mode-selection" class="toolbar-button-group"> |
||||
<li> |
||||
<label for="work-packages-list-view-button" class="hidden-for-sighted"> |
||||
{{ getActivationActionLabel(isDetailsViewActive()) + ' ' + I18n.t('js.button_list_view') }} |
||||
</label> |
||||
<button id="work-packages-list-view-button" |
||||
accesskey="{{ !isDetailsViewActive() ? '' : '8' }}" |
||||
class="button" |
||||
title="{{ getActivationActionLabel(isDetailsViewActive()) + ' ' + I18n.t('js.button_list_view') }}" |
||||
ng-click="closeDetailsView()" |
||||
ng-class="{ '-active': !isDetailsViewActive() }"> |
||||
<i class="icon-table-view button--icon"></i> |
||||
</button> |
||||
</li> |
||||
<li feature-flag="detailsView"> |
||||
<label for="work-packages-details-view-button" class="hidden-for-sighted"> |
||||
{{ getActivationActionLabel(!isDetailsViewActive()) + ' ' + I18n.t('js.button_details_view') }} |
||||
</label> |
||||
<button class="hide" |
||||
ng-click="openOverviewTab()" |
||||
accesskey="{{ isDetailsViewActive() ? '' : '8' }}"></button> |
||||
<button id="work-packages-details-view-button" |
||||
class="button" |
||||
title="{{ getActivationActionLabel(!isDetailsViewActive()) + ' ' + I18n.t('js.button_details_view') }}" |
||||
ng-class="{ '-active': isDetailsViewActive() }" |
||||
ng-click="openLatestTab()"> |
||||
<i class="icon-table-detail-view button--icon"></i> |
||||
</button> |
||||
</li> |
||||
<li> |
||||
<label for="work-packages-show-view-button" class="hidden-for-sighted"> |
||||
{{ getActivationActionLabel(!isShowViewActive()) + ' ' + I18n.t('js.button_show_view') }} |
||||
</label> |
||||
<button id="work-packages-list-view-button" |
||||
accesskey="{{ isShowViewActive() ? '' : '9' }}" |
||||
class="button" |
||||
title="{{ getActivationActionLabel(!isShowViewActive()) + ' ' + I18n.t('js.button_show_view') }}" |
||||
ng-click="showWorkPackageShowView()" |
||||
ng-class="{ '-active': isShowViewActive() }"> |
||||
<i class="icon-table-view button--icon"></i> |
||||
</button> |
||||
</li> |
||||
</ul> |
||||
</li> |
||||
<li class="toolbar-item"> |
||||
<label for="work-packages-settings-button" class="hidden-for-sighted"> |
||||
{{ I18n.t('js.button_settings') }} |
||||
</label> |
||||
<button id="work-packages-settings-button" |
||||
title="{{ I18n.t('js.button_settings') }}" |
||||
class="button last work-packages-settings-button" |
||||
has-dropdown-menu |
||||
target="SettingsDropdownMenu" |
||||
locals="query"> |
||||
<i class="button--icon icon-settings"></i> |
||||
<i class="button--dropdown-indicator"></i> |
||||
</button> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="work-packages--filters-optional-container" ng-show="showFiltersOptions"> |
||||
<div query-form id="query_form_content" class="hide-when-print"> |
||||
<query-filters></query-filters> |
||||
</div> |
||||
</div> |
||||
|
||||
<back-url></back-url> |
||||
|
||||
|
||||
<div class="work-packages--split-view"> |
||||
<div class="work-packages--left"> |
||||
<div class="attributes-group"> |
||||
|
||||
<div class="attributes-group--header"> |
||||
<div class="attributes-group--header-container"> |
||||
<h3 class="attributes-group--header-text"> |
||||
{{ I18n.t('js.label_description') }} |
||||
</h3> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="single-attribute wiki"> |
||||
<work-package-field field="'description'"></work-package-field> |
||||
</div> |
||||
</div> |
||||
|
||||
<div ng-repeat="group in vm.groupedFields" ng-hide="vm.hideEmptyFields && vm.isGroupHideable(vm.groupedFields, group.groupName, vm.workPackage)" class="attributes-group"> |
||||
|
||||
<div class="attributes-group--header"> |
||||
<div class="attributes-group--header-container"> |
||||
<h3 class="attributes-group--header-text" |
||||
ng-bind="I18n.t('js.work_packages.property_groups.' + group.groupName)"></h3> |
||||
</div> |
||||
<div class="attributes-group--header-toggle"> |
||||
<panel-expander tabindex="-1" ng-if="vm.showToggleButton() && $first" |
||||
collapsed="vm.hideEmptyFields" |
||||
expand-text="{{ I18n.t('js.label_show_attributes') }}" |
||||
collapse-text="{{ I18n.t('js.label_hide_attributes') }}"> |
||||
</panel-expander> |
||||
</div> |
||||
</div> |
||||
|
||||
<dl class="attributes-key-value"> |
||||
<dt |
||||
ng-hide="vm.hideEmptyFields && vm.isFieldHideable(vm.workPackage, field)" |
||||
ng-if="vm.isSpecified(vm.workPackage, field)" |
||||
ng-repeat-start="field in group.attributes" class="attributes-key-value--key"> |
||||
{{vm.getLabel(vm.workPackage, field)}} |
||||
<span class="required" ng-if="vm.hasNiceStar(vm.workPackage, field)"> *</span> |
||||
</dt> |
||||
<dd |
||||
ng-hide="vm.hideEmptyFields && vm.isFieldHideable(vm.workPackage, field)" |
||||
ng-if="vm.isSpecified(vm.workPackage, field)" |
||||
ng-repeat-end |
||||
class="attributes-key-value--value-container"> |
||||
<work-package-field field="field"></work-package-field> |
||||
</dd> |
||||
</dl> |
||||
</div> |
||||
|
||||
<work-package-attachments edit work-package="vm.workPackage"></work-package-attachments> |
||||
|
||||
</div> |
||||
<div class="work-packages--right"> |
||||
<div id="tabs"> |
||||
<ul class="tabrow"> |
||||
<!-- The hrefs with empty URLs are necessary for IE10 to focus these links |
||||
properly. Thus, don't remove the hrefs or the empty URLs! --> |
||||
<li ui-sref="work-packages.show.activity({})" |
||||
ui-sref-active="selected"> |
||||
<a href="" ng-bind="I18n.t('js.work_packages.tabs.activity')"/> |
||||
</li> |
||||
<li ui-sref="work-packages.show.relations({})" |
||||
ui-sref-active="selected"> |
||||
<a href="" ng-bind="I18n.t('js.work_packages.tabs.relations')"/> |
||||
</li> |
||||
<li ng-if="canViewWorkPackageWatchers()" |
||||
ui-sref="work-packages.show.watchers({})" |
||||
ui-sref-active="selected"> |
||||
<a href="" ng-bind="I18n.t('js.work_packages.tabs.watchers')"/> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
<div class="tabcontent" ui-view> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -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 |
||||
); |
||||
}; |
Loading…
Reference in new issue