@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
250 lines (248 loc) • 10.1 kB
JavaScript
/*
* 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;
})
);
}