UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

250 lines (248 loc) 10.1 kB
/* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as published by * the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * * 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, see <http://www.gnu.org/licenses/>. */ import { errorSelectorApi1, get, postJSON } from '../utils/ajax'; import { catchError, map } from 'rxjs/operators'; import { deserialize, entityEncodingTagValueProcessor, fromString, getInnerHtml } from '../utils/xml'; import { reversePluckProps, toQueryString } from '../utils/object'; import { asArray } from '../utils/array'; export function fetchConfigurationXML(site, configPath, module, environment) { const qs = toQueryString({ siteId: site, module, path: configPath, environment }); return get(`/studio/api/2/configuration/get_configuration${qs}`).pipe(map((response) => response?.response?.content)); } export function fetchConfigurationDOM(site, configPath, module, environment) { return fetchConfigurationXML(site, configPath, module, environment).pipe(map(fromString)); } /** * Fetches the requested configPath XML and deserializes such XML into JSON * @param site {string} The site id from which to fetch the configuration from * @param configPath {string} The path — inside the module — to the configuration file to fetch * @param module {engine | studio} The module from which to fetch the configuration from * @param environment {string} Optional environment to fetch the configuration from */ export function fetchConfigurationJSON(site, configPath, module, environment) { return fetchConfigurationXML(site, configPath, module, environment).pipe( map((conf) => { return deserialize(conf, { parseTagValue: false, tagValueProcessor: entityEncodingTagValueProcessor }); }) ); } /** * Persists the content of a configuration file. * @param site {string} The site id from which to fetch the configuration from. * @param partialPath {string} The path *inside the module*, excluding root (/config) and module (/studio|engine). If full path is `/config/studio/ui.xml`, partial path `/ui.xml`. * @param module {engine | studio} The module that owns this configuration file. * @param content {string} The content to write. * @param environment {string} Optional environment to write to. **/ export function writeConfiguration(site, partialPath, module, content, environment) { return postJSON('/studio/api/2/configuration/write_configuration', { siteId: site, module, path: partialPath, content, ...(environment && { environment }) }).pipe(map(() => true)); } // TODO: asses the location of profile methods. export function fetchActiveTargetingModel(site) { return get(`/api/1/profile/get`).pipe( map((response) => { const data = reversePluckProps(response.response, 'id'); const id = response.response.id ?? null; return { craftercms: { id, path: null, label: null, locale: null, dateCreated: null, dateModified: null, contentTypeId: null, disabled: false, sourceMap: {} }, ...data }; }) ); } export function deserializeActiveTargetingModelData(data, contentTypeFields) { return { craftercms: { id: '', path: null, label: null, dateCreated: null, dateModified: null, contentTypeId: null, disabled: false, sourceMap: {} }, ...data }; } export function setActiveTargetingModel(data) { const model = reversePluckProps(data, 'craftercms'); const qs = toQueryString({ id: data.craftercms.id }); return postJSON(`/api/1/profile/set${qs}`, model).pipe(map((response) => response?.response)); } // endregion export function fetchSiteUiConfig(site, environment) { return fetchConfigurationXML(site, '/ui.xml', 'studio', environment); } const legacyToNextMenuIconMap = { 'fa-sitemap': 'craftercms.icons.Sites', 'fa-user': '@mui/icons-material/PeopleRounded', 'fa-users': '@mui/icons-material/SupervisedUserCircleRounded', 'fa-database': '@mui/icons-material/StorageRounded', 'fa-bars': '@mui/icons-material/SubjectRounded', 'fa-level-down': '@mui/icons-material/SettingsApplicationsRounded', 'fa-align-left': '@mui/icons-material/FormatAlignCenterRounded', 'fa-globe': '@mui/icons-material/PublicRounded', 'fa-lock': '@mui/icons-material/LockRounded', 'fa-key': '@mui/icons-material/VpnKeyRounded' }; export function fetchGlobalMenuItems() { return get('/studio/api/2/ui/views/global_menu.json').pipe( map((response) => { const menuItems = response?.response?.menuItems ?? []; return [ ...menuItems.map((item) => ({ ...item, icon: legacyToNextMenuIconMap[item.icon] ? { id: legacyToNextMenuIconMap[item.icon] } : { baseClass: item.icon.includes('fa') ? `fa ${item.icon}` : item.icon } })), { id: 'home.globalMenu.about-us', icon: { id: 'craftercms.icons.About' }, label: 'About' }, { id: 'home.globalMenu.settings', icon: { id: '@mui/icons-material/AccountCircleRounded' }, label: 'Account' } ]; }) ); } export function fetchProductLanguages() { return get('/studio/api/1/services/api/1/server/get-available-languages.json').pipe( map((response) => response?.response) ); } export function fetchHistory(site, path, environment, module) { const parsedPath = encodeURIComponent(path.replace(/(\/config\/)(studio|engine)/g, '')); return get( `/studio/api/2/configuration/get_configuration_history.json?siteId=${site}&path=${parsedPath}&environment=${environment}&module=${module}` ).pipe(map((response) => response?.response.history.versions)); } export function fetchCannedMessage(site, locale, type) { return get( `/studio/api/1/services/api/1/site/get-canned-message.json?site=${site}&locale=${locale}&type=${type}` ).pipe( map((response) => response?.response), catchError(errorSelectorApi1) ); } export function fetchSiteLocale(site, environment) { return fetchSiteConfigDOM(site, environment).pipe( map((xml) => { let settings = {}; if (xml) { const localeXML = xml.querySelector('locale'); if (localeXML) { settings = deserialize(localeXML).locale; } } return settings; }) ); } export function fetchSiteConfigurationFiles(site, environment) { return fetchConfigurationDOM(site, '/administration/config-list.xml', 'studio', environment).pipe( map((xml) => { let files = []; if (xml) { const filesXML = xml.querySelector('files'); if (filesXML) { files = deserialize(filesXML).files.file; } } return asArray(files); }) ); } export function fetchSiteConfig(site, environment) { return fetchSiteConfigDOM(site, environment).pipe( map((dom) => ({ site, cdataEscapedFieldPatterns: Array.from(dom.querySelectorAll('cdata-escaped-field-patterns > pattern')) .map(getInnerHtml) .filter(Boolean), upload: ((node) => (node ? deserialize(node).upload : {}))(dom.querySelector(':scope > upload')), locale: ((node) => (node ? deserialize(node).locale : {}))(dom.querySelector(':scope > locale')), remoteGitBranch: ((node) => (node ? deserialize(node).remoteGitBranch : null))( dom.querySelector(':scope > remoteGitBranch') ), publishing: ((node) => { const commentSettings = Object.assign({ required: false }, deserialize(node)?.publishing?.comments); return { publishCommentRequired: commentSettings['publishing-required'] ?? commentSettings.required, deleteCommentRequired: commentSettings['delete-required'] ?? commentSettings.required, bulkPublishCommentRequired: commentSettings['bulk-publish-required'] ?? commentSettings.required, publishByCommitCommentRequired: commentSettings['publish-by-commit-required'] ?? commentSettings.required, publishEverythingCommentRequired: commentSettings['publish-everything-required'] ?? commentSettings.required, submissionCommentMaxLength: commentSettings['submission-max-length'] ?? 500 }; })(dom.querySelector(':scope > publishing')) })) ); } function fetchSiteConfigDOM(site, environment) { return fetchConfigurationDOM(site, '/site-config.xml', 'studio', environment); } export function fetchCannedMessages(site, environment) { return fetchConfigurationDOM(site, '/workflow/notification-config.xml', 'studio', environment).pipe( map((dom) => { const cannedMessages = []; dom.querySelectorAll('cannedMessages > content').forEach((tag) => { cannedMessages.push({ key: tag.getAttribute('key'), title: tag.getAttribute('title'), message: getInnerHtml(tag) }); }); return cannedMessages; }) ); }