public/lib/composer-service.js (200 lines of code) (raw):

import angular from 'angular'; import './telemetry-service'; import { getListElementFormatFromLabel, contentTypeToComposerContentType, listElementArticleFormats } from './model/special-formats.ts'; angular.module('wfComposerService', ['wfTelemetryService']) .service('wfComposerService', ['$http', '$q', 'config', '$log', 'wfHttpSessionService', 'wfTelemetryService', wfComposerService]); function wfComposerService($http, $q, config, $log, wfHttpSessionService, wfTelemetryService) { const request = wfHttpSessionService.request; const composerContentFetch = config.composerContentDetails; function composerUpdateFieldUrl(fieldName, contentId) { function liveOrPreview(isPreview) { return `${composerContentFetch}${contentId}/${isPreview ? "preview" : "live"}/fields/${fieldName}` } return { preview: liveOrPreview(true), live: liveOrPreview(false) } } function composerUpdateSettingUrl(settingName, contentId) { function liveOrPreview(isPreview) { return `${composerContentFetch}${contentId}/${isPreview ? "preview" : "live"}/settings/${settingName}` } return { preview: liveOrPreview(true), live: liveOrPreview(false) } } // budget composer url parser - just gets the portion after the last '/' function parseComposerId(url) { return url.substring(url.lastIndexOf('/') + 1); } function deepSearch(obj, path) { if (path.length === 0) return obj; const next = path[0]; if (obj[next]) return deepSearch(obj[next], path.slice(1)); else return null; } // Mapping of workflow content fields to transform functions on composer response const composerParseMap = { composerId: (d) => d.id, contentType: (d) => d.type, articleFormat: (d) => { const displayHint = deepSearch(d, ['preview', 'data', 'settings', 'displayHint', 'data']) return listElementArticleFormats.find(f => f.value === displayHint)?.label }, headline: (d) => deepSearch(d, ['preview', 'data', 'fields', 'headline', 'data']) || undefined, published: (d) => d.published, timePublished: (d) => new Date(deepSearch(d, ['contentChangeDetails', 'data', 'published', 'date']) || undefined), path: (d) => deepSearch(d, ['identifiers', 'path', 'data']), commentable: (d) => deepSearch(d, ['preview', 'data', 'settings', 'commentable', 'data']) === 'true', takenDown: (d) => false, // TODO: takenDown from composer feed activeInInCopy: (d) => deepSearch(d, ['toolSettings', 'activeInInCopy', 'data']) === 'true', composerProdOffice: (d) => deepSearch(d, ['preview', 'data', 'settings', 'productionOffice', 'data']) || undefined, optimisedForWeb: (d) => deepSearch(d, ['toolSettings', 'seoOptimised', 'data']) === 'true', optimisedForWebChanged: (d) => deepSearch(d, ['toolSettings', 'seoChanged', 'data']) === 'true', revision: (d) => deepSearch(d, ['contentChangeDetails', 'data', 'revision']), lastModified: (d) => new Date(deepSearch(d, ['contentChangeDetails', 'data', 'lastModified', 'date']) || undefined), lastModifiedBy: (d) => deepSearch(d, ['contentChangeDetails', 'data', 'lastModified', 'user', 'firstName']) + ' ' + deepSearch(d, ['contentChangeDetails', 'data', 'lastModified', 'user', 'lastName']), commissionedLength: (d) => deepSearch(d, ['preview', 'data', 'fields', 'commissionedLength', 'data']) || undefined, missingCommissionedLengthReason: (d) => deepSearch(d, ['preview', 'data', 'fields', 'missingCommissionedLengthReason', 'data']) || undefined, displayHint: (d) => deepSearch(d, ['preview', 'data', 'settings', 'displayHint', 'data']) || undefined, }; function parseComposerData(response, target) { target = target || {}; if (!response.data || !response.data.data || !response.data.data.id) { $log.error("Composer response missing id field. Response: " + JSON.stringify(response) + " \n Stub metadata: " + JSON.stringify(target)) return Promise.reject({ message: "composer response did not contain id, response: " + JSON.stringify(response)}) } else { const data = response.data.data; Object.keys(composerParseMap).forEach((key) => { target[key] = composerParseMap[key](data); }); return Promise.resolve(target); } } function getComposerContent(url) { return $http({ method: 'GET', url: composerContentFetch + parseComposerId(url), params: {'includePreview': 'true'}, withCredentials: true }); } this.getComposerContent = getComposerContent; this.parseComposerData = parseComposerData; this.create = function createInComposer(type, commissioningDesks, commissionedLength, prodOffice, template, articleFormat, priority, missingCommissionedLengthReason) { var selectedDisplayHint = getListElementFormatFromLabel(articleFormat)?.value; var params = { 'type': contentTypeToComposerContentType(type), 'tracking': commissioningDesks, 'productionOffice': prodOffice, 'displayHint': selectedDisplayHint, 'originatingSystem': 'workflow' }; if(commissionedLength) params['initialCommissionedLength'] = commissionedLength; if(missingCommissionedLengthReason) params['missingCommissionedLengthReason'] = missingCommissionedLengthReason; if(template) { params['template'] = template.id; } const commissioningDeskExternalName = _wfConfig.commissioningDesks .find(desk => desk.id.toString() === commissioningDesks)?.externalName; const getPriorityName = (priority) => { switch (priority){ case 0: return 'Normal' case 1: return 'Urgent' case 2: return 'Very Urgent' default: return 'Unknown' } } const tags = { contentType: contentTypeToComposerContentType(type), productionOffice: prodOffice, priority: getPriorityName(priority), } if(selectedDisplayHint !== null && selectedDisplayHint !== undefined) tags.displayHint = selectedDisplayHint; if(commissionedLength !== null && commissionedLength !== undefined) tags.commissionedLength = commissionedLength.toString(); if(commissioningDeskExternalName !== null && commissioningDeskExternalName !== undefined) tags.commissioningDesk = commissioningDeskExternalName; if(missingCommissionedLengthReason !== null && missingCommissionedLengthReason !== undefined) tags.missingCommissionedLengthReason = missingCommissionedLengthReason; wfTelemetryService.sendTelemetryEvent("WORKFLOW_CREATE_IN_COMPOSER_TRIGGERED", tags); return request({ method: 'POST', url: config.composerNewContent, params: params, withCredentials: true }); }; this.loadTemplates = function() { return request({ method: 'GET', url: config.composerTemplates, withCredentials: true }).then(({ data }) => { return data; }); }; this.updateField = function (composerId, fieldName, value, live = false) { let urls = composerUpdateFieldUrl(fieldName, composerId); let url = live ? urls.live : urls.preview; let req = { method: 'PUT', url: url, data: `"${value}"`, withCredentials: true }; return request(req); }; this.updateFieldInPreviewAndLive = function (composerId, fieldName, value) { return Promise.all([ this.updateField(composerId, fieldName, value, false), this.updateField(composerId, fieldName, value, true) ]); } this.updateSetting = function (composerId, settingName, value, live = false) { let urls = composerUpdateSettingUrl(settingName, composerId); let url = live ? urls.live : urls.preview; let req = { method: 'PUT', url: url, data: `"${value}"`, withCredentials: true }; return request(req); }; this.updateSettingInPreviewAndLive = function (composerId, settingName, value) { return Promise.all([ this.updateSetting(composerId, settingName, value, false), this.updateSetting(composerId, settingName, value, true) ]); } this.deleteField = function (composerId, fieldName, live = false) { let urls = composerUpdateFieldUrl(fieldName, composerId); let url = live ? urls.live : urls.preview; let req = { method: 'DELETE', url: url, withCredentials: true }; return request(req); }; this.deleteFieldInPreviewAndLive = function (composerId, fieldName) { return Promise.all([ this.deleteField(composerId, fieldName, false), this.deleteField(composerId, fieldName, true) ]); } /** * Update rights information for the given piece. * @param {string} composerId * @param {{ * developerCommunity: boolean, * subscriptionDatabases: boolean, * syndicationAggregate: boolean * }} rightsData * @returns Promise<Response> */ this.updateRights = function ( composerId, rightsData ) { let req = { method: 'PUT', url: `${composerContentFetch}${composerId}/rights`, data: JSON.stringify(rightsData), withCredentials: true }; return request(req); }; }