Merge pull request #1716 from opf/feature/refactor_angular_relations
[Feature] Refactor work package relations implementationpull/1699/merge
commit
26c3975608
@ -0,0 +1,37 @@ |
||||
//-- 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.workPackages.tabs') |
||||
|
||||
.directive('addWorkPackageRelation', [function() { |
||||
return { |
||||
restrict: 'E', |
||||
templateUrl: '/templates/work_packages/tabs/_add_work_package_relation.html', |
||||
}; |
||||
}]); |
@ -1,84 +0,0 @@ |
||||
//-- 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.workPackages.tabs') |
||||
|
||||
.directive('workPackageChildren', [ |
||||
'I18n', |
||||
'PathHelper', |
||||
'WorkPackageService', |
||||
'WorkPackagesHelper', |
||||
'$timeout', |
||||
function(I18n, PathHelper, WorkPackageService, WorkPackagesHelper, $timeout) { |
||||
return { |
||||
restrict: 'E', |
||||
replace: true, |
||||
scope: { |
||||
title: '@', |
||||
workPackage: '=', |
||||
children: '=', |
||||
btnTitle: '@buttonTitle', |
||||
btnIcon: '@buttonIcon' |
||||
}, |
||||
templateUrl: '/templates/work_packages/tabs/_work_package_children.html', |
||||
link: function(scope, element, attrs) { |
||||
scope.I18n = I18n; |
||||
scope.userPath = PathHelper.staticUserPath; |
||||
scope.workPackagePath = PathHelper.staticWorkPackagePath; |
||||
scope.getState = WorkPackagesHelper.getState; |
||||
scope.getFullIdentifier = WorkPackagesHelper.getFullIdentifier; |
||||
|
||||
var setExpandState = function() { |
||||
scope.expand = scope.children && scope.children.length > 0; |
||||
}; |
||||
|
||||
scope.$watch('children', function() { |
||||
setExpandState(); |
||||
scope.childrenCount = scope.children.length || 0; |
||||
}); |
||||
|
||||
scope.$watch('expand', function(newVal, oldVal) { |
||||
scope.stateClass = WorkPackagesHelper.collapseStateIcon(!newVal); |
||||
}); |
||||
|
||||
scope.toggleExpand = function() { |
||||
scope.expand = !scope.expand; |
||||
}; |
||||
|
||||
scope.addChild = function() { |
||||
// Temporarily go to old create view with parent_id set to currently viewed work package
|
||||
window.location = PathHelper.staticWorkPackageNewWithParentPath(scope.workPackage.props.projectId, scope.workPackage.props.id); |
||||
} |
||||
|
||||
scope.deleteChild = function() { |
||||
//TODO: Requires API endpoint for update work package
|
||||
} |
||||
} |
||||
}; |
||||
}]); |
@ -0,0 +1,144 @@ |
||||
//-- 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.viewModels') |
||||
|
||||
.factory('CommonRelationsHandler', [ |
||||
'$timeout', |
||||
'WorkPackageService', |
||||
'ApiHelper', |
||||
function($timeout, WorkPackageService, ApiHelper) { |
||||
function CommonRelationsHandler(workPackage, |
||||
relations, |
||||
relationsId) { |
||||
this.workPackage = workPackage; |
||||
this.relations = relations; |
||||
this.relationsId = relationsId; |
||||
|
||||
this.type = "relation"; |
||||
this.isSingletonRelation = false; |
||||
} |
||||
|
||||
CommonRelationsHandler.prototype = { |
||||
isEmpty: function() { |
||||
return !this.relations || this.relations.length === 0; |
||||
}, |
||||
|
||||
getCount: function() { |
||||
return (this.relations) ? this.relations.length : 0; |
||||
}, |
||||
|
||||
canAddRelation: function() { |
||||
return !!this.workPackage.links.addRelation; |
||||
}, |
||||
|
||||
addRelation: function(scope) { |
||||
var inputElement = angular.element('#relation_to_id-' + this.relationsId); |
||||
var toId = inputElement.val(); |
||||
WorkPackageService.addWorkPackageRelation(this.workPackage, toId, this.relationsId).then(function(relation) { |
||||
inputElement.val(''); |
||||
scope.$emit('workPackageRefreshRequired', ''); |
||||
}, function(error) { |
||||
ApiHelper.handleError(scope, error); |
||||
}); |
||||
}, |
||||
|
||||
applyCustomExtensions: function() { |
||||
// Massive hack alert - Using old prototype autocomplete ///////////
|
||||
if(this.canAddRelation) { |
||||
var workPackage = this.workPackage; |
||||
var relationsId = this.relationsId; |
||||
|
||||
$timeout(function() { |
||||
var url = PathHelper.workPackageAutoCompletePath(workPackage.props.projectId, workPackage.props.id); |
||||
new Ajax.Autocompleter('relation_to_id-' + relationsId, |
||||
'related_issue_candidates-' + relationsId, |
||||
url, |
||||
{ minChars: 1, |
||||
frequency: 0.5, |
||||
paramName: 'q', |
||||
updateElement: function(value) { |
||||
document.getElementById('relation_to_id-' + relationsId).value = value.id; |
||||
}, |
||||
parameters: 'scope=all' |
||||
}); |
||||
}); |
||||
} |
||||
////////////////////////////////////////////////////////////////////
|
||||
}, |
||||
|
||||
getRelatedWorkPackage: function(workPackage, relation) { |
||||
var self = workPackage.links.self.href; |
||||
|
||||
if (relation.links.relatedTo.href == self) { |
||||
return relation.links.relatedFrom.fetch(); |
||||
} else { |
||||
return relation.links.relatedTo.fetch(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
return CommonRelationsHandler; |
||||
}]) |
||||
|
||||
.factory('ChildrenRelationsHandler', ['PathHelper', |
||||
'CommonRelationsHandler', |
||||
function(PathHelper, |
||||
CommonRelationsHandler) { |
||||
function ChildrenRelationsHandler(workPackage, children) { |
||||
var handler = new CommonRelationsHandler(workPackage, children, undefined); |
||||
|
||||
handler.type = "child"; |
||||
handler.canAddRelation = function() { return true }; |
||||
handler.addRelation = function() { |
||||
window.location = PathHelper.staticWorkPackageNewWithParentPath(this.workPackage.props.projectId, this.workPackage.props.id); |
||||
}; |
||||
handler.applyCustomExtensions = undefined; |
||||
handler.getRelatedWorkPackage = function(workPackage, relation) { return relation.fetch() }; |
||||
|
||||
return handler; |
||||
} |
||||
|
||||
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; |
||||
}]); |
@ -0,0 +1,5 @@ |
||||
<button class="button" |
||||
title="{{ btnTitle }}" |
||||
ng-bind-html="btnIcon + ' ' + btnTitle" |
||||
ng-click="handler.addRelation()"> |
||||
</button> |
@ -0,0 +1,12 @@ |
||||
<button class="button" |
||||
title="{{ btnTitle }}" |
||||
ng-bind-html="btnIcon + ' ' + btnTitle" |
||||
ng-click="handler.addRelation(this)"> |
||||
</button> |
||||
<input id="relation_to_id-{{ handler.relationsId }}" |
||||
name="relation[to_id][{{ handler.relationsId }}]" |
||||
size="10" |
||||
type="text" |
||||
autocomplete="off"> |
||||
<div id="related_issue_candidates-{{ handler.relationsId }}" |
||||
class="autocomplete related-issue-candidates"></div> |
@ -1,48 +0,0 @@ |
||||
<div class="relation"> |
||||
<h3> |
||||
<accessible-by-keyboard execute="toggleExpand()"> |
||||
<i class="icon-pull-content" ng-class="stateClass"></i> {{ title }} |
||||
<span ng-if="!isSingletonRelation">({{ childrenCount }})</span> |
||||
</accessible-by-keyboard> |
||||
</h3> |
||||
<div class="content" ng-show="expand"> |
||||
<div class="workpackages"> |
||||
<div ng-if="children"> |
||||
<table> |
||||
<thead> |
||||
<tr> |
||||
<td>{{ I18n.t('js.work_packages.properties.subject') }}</td> |
||||
<td>{{ I18n.t('js.work_packages.properties.status') }}</td> |
||||
<td>{{ I18n.t('js.work_packages.properties.assignee') }}</td> |
||||
<td></td> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr ng-repeat="workPackage in children"> |
||||
<td> |
||||
<a title="{{ getFullIdentifier(workPackage) }}" class="work_package" ng-class="getState(workPackage)" href="{{ workPackagePath(workPackage.props.id) }}"> |
||||
{{ getFullIdentifier(workPackage) }} |
||||
</a> |
||||
</td> |
||||
<td title="{{ workPackage.props.status }}">{{ workPackage.props.status }}</td> |
||||
<td> |
||||
<a title="{{ workPackage.embedded.assignee.props.name }}" href="{{ userPath(workPackage.embedded.assignee.props.id) }}"> |
||||
{{ workPackage.embedded.assignee.props.name }} |
||||
</a> |
||||
</td> |
||||
<td><!--i title="delete relation" class="delete-item icon-delete" ng-click="removeChild()"></i--></td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
<div ng-if="!children || children.length === 0"> |
||||
No child work packages |
||||
</div> |
||||
</div> |
||||
<button class="button" |
||||
title="{{ btnTitle }}" |
||||
ng-bind-html="btnIcon + ' ' + btnTitle" |
||||
ng-click="addChild()"> |
||||
</button> |
||||
</div> |
||||
</div> |
@ -1,42 +0,0 @@ |
||||
<div class="relation"> |
||||
<h3> |
||||
<accessible-by-keyboard execute="toggleExpand()"> |
||||
<i class="icon-pull-content" ng-class="stateClass"></i> {{ title }} |
||||
</accessible-by-keyboard> |
||||
</h3> |
||||
<div class="content" ng-show="expand"> |
||||
<div class="workpackages"> |
||||
<div ng-if="parent"> |
||||
<table> |
||||
<thead> |
||||
<tr> |
||||
<td>{{ I18n.t('js.work_packages.properties.subject') }}</td> |
||||
<td>{{ I18n.t('js.work_packages.properties.status') }}</td> |
||||
<td>{{ I18n.t('js.work_packages.properties.assignee') }}</td> |
||||
<td></td> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td> |
||||
<a title="{{ getFullIdentifier(parent) }}" class="work_package" ng-class="getState(parent)" href="{{ workPackagePath(parent.props.id) }}"> |
||||
{{ getFullIdentifier(parent) }} |
||||
</a> |
||||
</td> |
||||
<td title="{{ parent.props.status }}">{{ parent.props.status }}</td> |
||||
<td> |
||||
<a title="{{ parent.embedded.assignee.props.name }}" href="{{ userPath(parent.embedded.assignee.props.id) }}"> |
||||
{{ parent.embedded.assignee.props.name }} |
||||
</a> |
||||
</td> |
||||
<td><!--i title="delete relation" class="delete-item icon-delete" ng-click="removeChild()"></i--></td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
<div ng-if="!parent"> |
||||
No parent work packages |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
Loading…
Reference in new issue