From c771456708aede57dfabe7e9c5b095b92b4d2134 Mon Sep 17 00:00:00 2001 From: Dmitry Kanashevich Date: Mon, 15 Jul 2013 15:44:25 +0300 Subject: [PATCH] Added URL Custom Field Tab mashup --- URL Custom Field Tab/URLCustomFieldTab.cfg | 1 + .../URLCustomFieldTab.config.js | 15 ++ URL Custom Field Tab/URLCustomFieldTab.css | 12 ++ URL Custom Field Tab/URLCustomFieldTab.js | 175 ++++++++++++++++++ 4 files changed, 203 insertions(+) create mode 100644 URL Custom Field Tab/URLCustomFieldTab.cfg create mode 100644 URL Custom Field Tab/URLCustomFieldTab.config.js create mode 100644 URL Custom Field Tab/URLCustomFieldTab.css create mode 100644 URL Custom Field Tab/URLCustomFieldTab.js diff --git a/URL Custom Field Tab/URLCustomFieldTab.cfg b/URL Custom Field Tab/URLCustomFieldTab.cfg new file mode 100644 index 0000000..66ae5b8 --- /dev/null +++ b/URL Custom Field Tab/URLCustomFieldTab.cfg @@ -0,0 +1 @@ +Placeholders:footerplaceholder diff --git a/URL Custom Field Tab/URLCustomFieldTab.config.js b/URL Custom Field Tab/URLCustomFieldTab.config.js new file mode 100644 index 0000000..f16446d --- /dev/null +++ b/URL Custom Field Tab/URLCustomFieldTab.config.js @@ -0,0 +1,15 @@ +tau.mashups + .addModule('URLCustomFieldTab.config', function() { + var urlCustomFieldTabConfig = { + tabs: [{ + entityTypeName: 'userStory', + customFieldName: 'Sample URL Custom Field tab for User Story of projects with Scrum process', + processName: 'Scrum' + },{ + entityTypeName: 'bug', + customFieldName: 'Sample URL Custom Field tab for Bug of any project' + }] + }; + + return urlCustomFieldTabConfig; + }); \ No newline at end of file diff --git a/URL Custom Field Tab/URLCustomFieldTab.css b/URL Custom Field Tab/URLCustomFieldTab.css new file mode 100644 index 0000000..fe91377 --- /dev/null +++ b/URL Custom Field Tab/URLCustomFieldTab.css @@ -0,0 +1,12 @@ +.url-custom-field-tab-frame { + width: 100%; + height: 700px; + overflow: scroll; + border: 1px solid #CCCCCC; +} + +.url-custom-field-empty { + color: #BBBBBB; + font-size: 11px; + display: block; +} \ No newline at end of file diff --git a/URL Custom Field Tab/URLCustomFieldTab.js b/URL Custom Field Tab/URLCustomFieldTab.js new file mode 100644 index 0000000..2f1b4fb --- /dev/null +++ b/URL Custom Field Tab/URLCustomFieldTab.js @@ -0,0 +1,175 @@ +tau.mashups + .addDependency('jQuery') + .addDependency('Underscore') + .addDependency('tp/general/view') + .addDependency('tp3/mashups/storage') + .addDependency('URLCustomFieldTab.config') + .addCSS('URLCustomFieldTab.css') + .addMashup(function ($, _, generalView, Storage, urlCustomFieldTabConfig) { + var URLCustomFieldTab = function () { + _.forEach(urlCustomFieldTabConfig.tabs, _.bind(this._addTab, this)); + }; + + URLCustomFieldTab.prototype = { + URL_CF_TYPES: { + URL: 'url', + TEMPLATED_URL: 'templatedurl' + }, + REQUEST_FIELDS_FOR_ENTITY: ['customFields', { + project: [{ + process: ['name', { + customFields: ['name', 'value', { + entityType: ['name']}] + }] + }] + }], + REQUEST_FIELDS_FOR_DEFAULT_PROCESS: ['name', { + customFields: ['name', 'value', { + entityType: ['name'] + }] + }], + $FRAME_TEMPLATE: '', + $EMPTY_TEMPLATE: 'Nothing to display in the Tab: the value of the \'${customFieldName}\' Custom Field is empty', + + _addTab: function (tabConfig) { + generalView.addTab( + tabConfig.customFieldName, + _.bind(this._tabContentIsRenderedHandler, this, tabConfig), + $.noop, + { + getViewIsSuitablePromiseCallback: _.bind(this._getViewIsSuitablePromise, this, tabConfig) + }); + }, + _tabContentIsRenderedHandler: function (tabConfig, contentElement, context) { + this._getContextEntityPromise(context).done(_.bind(function(entity){ + this._getProcessWithCFDefinitionsPromise(entity).done( + _.bind(this._buildTab, this, tabConfig, contentElement, entity) + ) + }, this) + ); + }, + _getContextEntityPromise: function(context){ + var contextEntityDeferred = $.Deferred(); + (new Storage()) + .getEntity() + .ofType(context.entity.type || context.entity.entityType.name) + .withId(context.entity.id) + .withFieldSetRestrictedTo(this.REQUEST_FIELDS_FOR_ENTITY) + .withCallOnDone(contextEntityDeferred.resolve) + .withCallOnFail(contextEntityDeferred.reject) + .execute(); + return contextEntityDeferred.promise(); + }, + _getProcessWithCFDefinitionsPromise: function(entity){ + var cfDefinitionsDeferred = $.Deferred(); + if (entity.project){ + cfDefinitionsDeferred.resolve(entity.project.process); + } else { + (new Storage()) + .getEntities() + .ofType('process') + .filteredBy({isDefault: 'true'}) + .withFieldSetRestrictedTo(this.REQUEST_FIELDS_FOR_DEFAULT_PROCESS) + .withCallOnDone(function(processes){ + var defaultProcess = processes[0]; + cfDefinitionsDeferred.resolve(defaultProcess); + }) + .withCallOnFail(cfDefinitionsDeferred.reject) + .execute(); + } + return cfDefinitionsDeferred.promise(); + }, + _buildTab: function (tabConfig, contentElement, entity, process) { + var cfDefinitions = process.customFields; + var tabCF = this._getTabCF(tabConfig, entity.customFields); + if (!tabCF) { + return; + } + if (!tabCF.value){ + this._appendEmptyToTabContent($(contentElement), tabConfig.customFieldName) + return; + } + var entityCFDefinition = this._getEntityCFDefinition(tabConfig, cfDefinitions); + var tabFrameUrl = this._getUrl(tabCF, entityCFDefinition); + if (!tabFrameUrl) { + return; + } + this._appendFrameToTabContent($(contentElement), tabFrameUrl); + }, + _getTabCF: function(tabConfig, customFields){ + return _.find(customFields, _.bind(function (cf) { + return tabConfig.customFieldName.toLowerCase() === cf.name.toLowerCase() + && _.contains(_.values(this.URL_CF_TYPES), cf.type.toLowerCase()); + }, this)); + }, + _getEntityCFDefinition: function(tabConfig, cfDefinitions){ + return _.filter(cfDefinitions, function(cfDefinition){ + return tabConfig.customFieldName.toLowerCase() === cfDefinition.name.toLowerCase() + && tabConfig.entityTypeName.toLowerCase() === cfDefinition.entityType.name.toLowerCase(); + }); + }, + _getUrl: function(tabCF, entityCFDefinition){ + var tabFrameUrl; + switch (tabCF.type.toLowerCase()) { + case this.URL_CF_TYPES.URL: + tabFrameUrl = tabCF.value.url; + break; + case this.URL_CF_TYPES.TEMPLATED_URL: + if (!entityCFDefinition){ + break; + } + tabFrameUrl = entityCFDefinition.value.replace(/\{0\}/, tabCF.value); + break; + } + + return tabFrameUrl; + }, + _appendFrameToTabContent: function($contentElement, tabFrameUrl){ + $.tmpl(this.$FRAME_TEMPLATE, {url: tabFrameUrl}).appendTo($contentElement); + }, + _appendEmptyToTabContent: function($contentElement, customFieldName){ + $.tmpl(this.$EMPTY_TEMPLATE, {customFieldName: customFieldName}).appendTo($contentElement); + }, + _getViewIsSuitablePromise: function(tabConfig, viewContext){ + var viewIsSuitableDeferred = $.Deferred(); + if (!this._isViewSuitableByEntity(tabConfig, viewContext.entity)){ + viewIsSuitableDeferred.resolve(false); + + } else { + this._getContextEntityPromise(viewContext) + .done(_.bind(function(entity){ + this._getProcessWithCFDefinitionsPromise(entity) + .done(_.bind(function(process){ + var viewIsSuitable = this._isViewSuitableByProcess(tabConfig, process); + viewIsSuitableDeferred.resolve(viewIsSuitable); + }, this)) + .fail(function(failData){ + viewIsSuitableDeferred.reject(failData) + }); + + }, this)) + .fail(function(failData){ + viewIsSuitableDeferred.reject(failData) + }); + } + return viewIsSuitableDeferred.promise(); + }, + _isViewSuitableByEntity: function(tabConfig, entity){ + return tabConfig.entityTypeName + && tabConfig.entityTypeName.toLowerCase() === entity.entityType.name.toLowerCase(); + }, + _isViewSuitableByProcess: function(tabConfig, process){ + return !tabConfig.processName + || (process + && tabConfig.processName.toLowerCase() === process.name.toLowerCase() + && _.find(process.customFields, function (cfDefinition) { + return tabConfig.customFieldName.toLowerCase() === cfDefinition.name.toLowerCase() + && tabConfig.entityTypeName.toLowerCase() === cfDefinition.entityType.name.toLowerCase(); + }) + ); + } + }; + + new URLCustomFieldTab(); + + }); \ No newline at end of file