kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.9 KiB
215 lines
6.9 KiB
//-- 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.
|
|
//++
|
|
|
|
function halTransformedElementService(Restangular:restangular.IService, $q:ng.IQService) {
|
|
return class HalTransformedElement {
|
|
constructor(protected element) {
|
|
return this.transform();
|
|
}
|
|
|
|
protected transform() {
|
|
if (!this.element._links && !this.element._embedded) return this.element;
|
|
|
|
if (!this.element.restangularized) {
|
|
this.element = Restangular.restangularizeElement(null, this.element, '');
|
|
}
|
|
|
|
const propertiesSet = [];
|
|
/**
|
|
* The properties added by the transformation should not be enumerable, so that
|
|
* Restangular's `.plain()` returns only the relevant properties.
|
|
*/
|
|
Object.defineProperties(this.element, {
|
|
/**
|
|
* Linked resources of the element.
|
|
*/
|
|
links: {value: this.transformLinks()},
|
|
|
|
/**
|
|
* Embedded resources of the element
|
|
*/
|
|
embedded: {value: this.transformEmbedded()},
|
|
|
|
/**
|
|
* Gets a linked or embedded resource and sets its plain value as a property of the
|
|
* element.
|
|
* Request the linked resource, if it's not embedded.
|
|
* @method
|
|
*/
|
|
//TODO: Add embedded resource handling (see description).
|
|
//TODO: Always return a promise.
|
|
setProperty: {
|
|
value: (propertyName:string) => {
|
|
return !!this.element.links[propertyName] && this.element.links[propertyName]().then(value => {
|
|
propertiesSet.push(propertyName);
|
|
return this.element[propertyName] = value;
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set linked or embedded resources as properties of the element.
|
|
* @method
|
|
*/
|
|
//TODO: Return a promise based on $q.all
|
|
setProperties: {
|
|
value: (propertyNames:string[]) => {
|
|
propertyNames.forEach(this.element.setProperty);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Write the linked property's value back to the original _links attribute.
|
|
* This is useful, if you want to save the resource.
|
|
* @method
|
|
*/
|
|
//TODO: Handle _embedded properties (it it makes any sense - probably not).
|
|
//TODO: Maybe delete the linked property, as it has no use.
|
|
data: {
|
|
value: () => {
|
|
var plain = this.element.plain();
|
|
plain._links = {};
|
|
|
|
|
|
angular.forEach(this.element.links, (link, name) => {
|
|
var property = this.element[name];
|
|
var source = link._source;
|
|
|
|
if (propertiesSet.indexOf(name) !== -1) {
|
|
if (property._links) {
|
|
property = new HalTransformedElement(property);
|
|
}
|
|
|
|
if (property.links.self) {
|
|
source = property.links.self._source;
|
|
}
|
|
}
|
|
|
|
plain._links[name] = source;
|
|
});
|
|
|
|
return plain;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Indicate whether the element has been transformed.
|
|
* @boolean
|
|
*/
|
|
halTransformed: {value: true}
|
|
});
|
|
|
|
angular.forEach(this.element.links, (link, linkName) => {
|
|
const property = {};
|
|
angular.extend(property, link._source);
|
|
Object.defineProperty(this.element, linkName, {value: property});
|
|
});
|
|
|
|
return this.element;
|
|
}
|
|
|
|
/**
|
|
* Transform links
|
|
*
|
|
* Links are methods that return a promise.
|
|
* Collections can be requested by `link[linkName].all()`.
|
|
*/
|
|
//TODO: Implement handling for link arrays (see schema.priority._links.allowedValues)
|
|
protected transformLinks() {
|
|
return this.transformHalProperty('_links', (links, link, linkName) => {
|
|
var method = (method:string, multiplier?:string = 'oneUrl') => {
|
|
return (...params) => {
|
|
if (method === 'post') params.unshift('');
|
|
if (link.href !== null) {
|
|
return this.element[multiplier](linkName, link.href)[method]
|
|
.apply(this.element, params)
|
|
.then(value => {
|
|
if (value) {
|
|
angular.extend(this.element[linkName], value);
|
|
}
|
|
return value;
|
|
});
|
|
}
|
|
|
|
return $q.when({});
|
|
}
|
|
};
|
|
|
|
if (!link.method) {
|
|
links[linkName] = method('get');
|
|
links[linkName].list = method('getList', 'allUrl');
|
|
}
|
|
else {
|
|
links[linkName] = method(link.method);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Transform embedded properties and their children to actual HAL resources,
|
|
* if they have links or embedded resources.
|
|
*/
|
|
protected transformEmbedded() {
|
|
return this.transformHalProperty('_embedded', (embedded, element, name) => {
|
|
angular.forEach(element, child => child && new HalTransformedElement(element));
|
|
embedded[name] = new HalTransformedElement(element);
|
|
});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param propertyName
|
|
* @param callback
|
|
* @returns {{}}
|
|
*/
|
|
protected transformHalProperty(propertyName:string, callback:(props, prop, name) => any) {
|
|
var properties = {};
|
|
var _properties = this.element[propertyName];
|
|
|
|
delete this.element[propertyName];
|
|
|
|
angular.forEach(_properties, (property, name) => {
|
|
callback(properties, property, name);
|
|
});
|
|
|
|
// Add a _source property so the information of the source is accessible
|
|
angular.forEach(properties, (property, name) => {
|
|
if (_properties[name]) {
|
|
Object.defineProperty(
|
|
properties[name], '_source', {value: angular.copy(_properties[name])});
|
|
}
|
|
});
|
|
|
|
return properties;
|
|
}
|
|
};
|
|
}
|
|
|
|
angular
|
|
.module('openproject.api')
|
|
.factory('HalTransformedElement', halTransformedElementService);
|
|
|