Skip to content

Commit

Permalink
IBX-6315: Edit/go to embeded item
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasOsti committed Sep 4, 2023
1 parent d4665de commit ac49197
Show file tree
Hide file tree
Showing 11 changed files with 461 additions and 23 deletions.
1 change: 1 addition & 0 deletions src/bundle/Resources/encore/ibexa.js.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const layout = [
path.resolve(__dirname, '../public/js/scripts/admin.back.to.top.js'),
path.resolve(__dirname, '../public/js/scripts/admin.middle.ellipsis.js'),
path.resolve(__dirname, '../public/js/scripts/admin.form.error.js'),
path.resolve(__dirname, '../public/js/scripts/embedded.item.actions'),
path.resolve(__dirname, '../public/js/scripts/widgets/flatpickr.js'),
];
const fieldTypes = [];
Expand Down
8 changes: 8 additions & 0 deletions src/bundle/Resources/public/js/scripts/admin.location.view.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
const sortContainer = doc.querySelector('[data-sort-field][data-sort-order]');
const sortField = sortContainer.getAttribute('data-sort-field');
const sortOrder = sortContainer.getAttribute('data-sort-order');
const emdedItemsUpdateChannel = new BroadcastChannel('ibexa-emded-item-live-update');
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const publishedContentId = urlParams.get('publishedContentId');
const handleEditItem = (content, location) => {
const contentId = content._id;
const locationId = location._id;
Expand Down Expand Up @@ -190,6 +194,10 @@
}),
);
});

if (publishedContentId) {
emdedItemsUpdateChannel.postMessage({ contentId: publishedContentId });
}
})(
window,
window.document,
Expand Down
314 changes: 314 additions & 0 deletions src/bundle/Resources/public/js/scripts/embedded.item.actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
(function (global, doc, ibexa, Routing, Translator, Popper) {
let nodesToUpdate = null;
const MENU_PROPS = {
placement: 'bottom-end',
fallbackPlacements: ['bottom-start', 'top-end', 'top-start'],
};
const token = document.querySelector('meta[name="CSRF-Token"]').content;
const siteaccess = document.querySelector('meta[name="SiteAccess"]').content;
const metaLanguageCode = document.querySelector('meta[name="LanguageCode"]')?.content;
const previewLanguageCode = metaLanguageCode ?? ibexa.adminUiConfig.languages.priority[0];
const adminUiLanguages = ibexa.adminUiConfig.languages.mappings;
const emdedItemsUpdateChannel = new BroadcastChannel('ibexa-emded-item-live-update');
const editEmbeddedItemForm = doc.querySelector('[name="embedded_item_edit"]');
const actionsMenuTriggerBtns = doc.querySelectorAll('.ibexa-embedded-item-actions__menu-trigger-btn');
const updateNode = ({ node, value, isMiddleEllipsis }) => {
if (!isMiddleEllipsis) {
node.innerHTML = value;

return;
}

const middleEllipsisNode = node.querySelector('.ibexa-middle-ellipsis');
const middleEllipsisNameStartNode = node.querySelector(
'.ibexa-middle-ellipsis__name--start .ibexa-middle-ellipsis__name-ellipsized',
);
const middleEllipsisNameEndNode = node.querySelector('.ibexa-middle-ellipsis__name--end .ibexa-middle-ellipsis__name-ellipsized');

middleEllipsisNode.title = value;
middleEllipsisNameStartNode.innerHTML = value;
middleEllipsisNameEndNode.innerHTML = value;

ibexa.helpers.ellipsis.middle.parse(node);
};
const updateNodes = async (contentId) => {
nodesToUpdate = doc.querySelectorAll(`[data-ibexa-update-content-id="${contentId}"]`);

if (!nodesToUpdate) {
return;
}

const contentData = await loadContentData(contentId);

[...nodesToUpdate].forEach((nodeToUpdate) => {
let sourceValue = contentData;
const { ibexaUpdateSourceDataPath } = nodeToUpdate.dataset;
const updateSourceDataPathArray = ibexaUpdateSourceDataPath.split('.');

for (const pathLevelIndex in updateSourceDataPathArray) {
const pathLevel = updateSourceDataPathArray[pathLevelIndex];

sourceValue = sourceValue[pathLevel];
}

if (sourceValue) {
const { ibexaUpdateMiddleEllipsis } = nodeToUpdate.dataset;

updateNode({
node: nodeToUpdate,
value: sourceValue,
isMiddleEllipsis: ibexaUpdateMiddleEllipsis,
});
}
});
};
const loadContentData = async (contentId) => {
try {
const loadContentRequest = new Request(`/api/ibexa/v2/content/objects/${contentId}`, {
method: 'GET',
headers: {
Accept: 'application/vnd.ibexa.api.Content+json',
'X-Siteaccess': siteaccess,
'X-CSRF-Token': token,
},
mode: 'same-origin',
credentials: 'same-origin',
});
const response = await fetch(loadContentRequest);

return ibexa.helpers.request.getJsonFromResponse(response);
} catch (error) {
ibexa.helpers.notification.showErrorNotification(error);
}
};
const editContent = ({ contentId, locationId, languageCode }) => {
if (!contentId || !locationId || !languageCode) {
return;
}

const contentInfoInput = editEmbeddedItemForm.querySelector('[name="embedded_item_edit[content_info]"]');
const locationInput = editEmbeddedItemForm.querySelector('[name="embedded_item_edit[location]"]');
const languageInput = editEmbeddedItemForm.querySelector(`[name="embedded_item_edit[language]"][value="${languageCode}"]`);

contentInfoInput.value = contentId;
locationInput.value = locationId;
languageInput.click();

editEmbeddedItemForm.submit();
};
const generateGoToActionItem = ({ contentId, locationId, productCode }) => {
const href = productCode
? Routing.generate('ibexa.product_catalog.product.view', {
productCode,
languageCode: previewLanguageCode,
})
: Routing.generate('ibexa.content.translation.view', {
contentId,
locationId,
languageCode: previewLanguageCode,
});

return {
label: Translator.trans(/*@Desc("Go to content")*/ 'embedded_items.action.go_to_label', {}, 'content'),
href,
};
};
const generateEditActionItem = ({ contentId, locationId, productCode, languages }) => {
if (languages.length > 1) {
return {
label: Translator.trans(/*@Desc("Edit")*/ 'embedded_items.action.edit', {}, 'content'),
branch: {
groups: [
{
id: 'edit-group',
items: languages.map(({ languageCode, name }) => {
const languageEditAction = productCode
? {
href: Routing.generate('ibexa.product_catalog.product.edit', {
productCode,
languageCode,
}),
}
: {
onClick: () => editContent({ contentId, locationId, languageCode }),
};

return {
label: name,
...languageEditAction,
};
}),
},
],
},
};
}

const editAction = productCode
? {
href: Routing.generate('ibexa.product_catalog.product.edit', {
productCode,
languageCode: languages[0].languageCode,
}),
}
: { onClick: () => editContent({ contentId, locationId, languageCode: languages[0].languageCode }) };

return {
label: Translator.trans(/*@Desc("Edit")*/ 'embedded_items.action.edit', {}, 'content'),
...editAction,
};
};
const generateMenuTreeItems = ({ contentId, locationId, productCode, languages }) => {
const goToItem = generateGoToActionItem({ contentId, locationId, productCode });
const editItem = generateEditActionItem({ contentId, locationId, productCode, languages });

return {
groups: [
{
id: 'default',
items: [goToItem, editItem],
},
],
};
};
const getLanguagesData = async ({ contentId, locationId, initialFunc = () => {}, callbackFunc = () => {} }) => {
try {
initialFunc();

const url = window.Routing.generate('ibexa.permission.limitation.language', {
contentId,
locationId,
versionNo: 0,
});
const request = new Request(url, {
method: 'GET',
headers: { 'X-CSRF-Token': token },
mode: 'same-origin',
credentials: 'same-origin',
});
const response = await fetch(request);
const data = await ibexa.helpers.request.getJsonFromResponse(response);

callbackFunc();

return data.filter((language) => language.hasAccess);
} catch (error) {
ibexa.helpers.notification.showErrorNotification(error);
}
};
const getMenuData = ({ container, event }) => {
const { contentId, locationId, productCode, languageCodes } = container ? container.dataset : event.detail;
const parsedLanguageCodes = typeof languageCodes === 'string' ? JSON.parse(languageCodes) : languageCodes;
const languages = parsedLanguageCodes
? parsedLanguageCodes.map((languageCode) => ({
languageCode,
name: adminUiLanguages[languageCode].name,
}))
: [];

return {
contentId: parseInt(contentId, 10),
locationId: parseInt(locationId, 10),
productCode,
languages,
};
};
const createMenu = async ({ triggerElement, container, contentId, locationId, productCode, languages }) => {
triggerElement.dataset.isMenuAttached = 1;

const mainContainer = container.closest('.ibexa-embedded-item-actions');
const menuLoader = mainContainer.querySelector('.ibexa-embedded-item-actions__loader');
const askForLanguagesData = Object.keys(languages).length !== 1;
const languagesData = askForLanguagesData
? await getLanguagesData({
contentId,
locationId,
initialFunc: showLoader.bind(null, { triggerElement, menuLoader }),
callbackFunc: hideLoader.bind(null, { menuLoader }),
})
: languages;
const menuItems = generateMenuTreeItems({ contentId, locationId, productCode, languages: languagesData });
const menuInstance = new ibexa.core.MultilevelPopupMenu({
container,
triggerElement,
});
menuInstance.init();
menuInstance.generateMenu({
triggerElement,
...MENU_PROPS,
...menuItems,
});

triggerElement.click();
};
const showLoader = ({ triggerElement, menuLoader }) => {
Popper.createPopper(triggerElement, menuLoader, {
placement: MENU_PROPS.placement,
modifiers: [
{
name: 'flip',
enabled: true,
options: {
fallbackPlacements: MENU_PROPS.fallbackPlacements,
},
},
],
});

menuLoader.classList.remove('ibexa-popup-menu--hidden');
};
const hideLoader = ({ menuLoader }) => {
menuLoader.classList.add('ibexa-popup-menu--hidden');
};

actionsMenuTriggerBtns.forEach((actionsMenuTriggerBtn) => {
actionsMenuTriggerBtn.addEventListener(
'click',
(event) => {
const isMenuAttached = !!parseInt(actionsMenuTriggerBtn.dataset.isMenuAttached, 10);

if (!isMenuAttached) {
event.preventDefault();

const menuMainContainer = actionsMenuTriggerBtn.closest('.ibexa-embedded-item-actions');
const menuContainer = menuMainContainer.querySelector('.ibexa-embedded-item-actions__menu');
const menuData = getMenuData({ container: menuContainer });

createMenu({
triggerElement: actionsMenuTriggerBtn,
container: menuContainer,
...menuData,
});
}
},
false,
);
});

doc.body.addEventListener('ibexa-embedded-item:create-dynamic-menu', (event) => {
const menuData = getMenuData({ event });
const { menuTriggerElement, menuContainer } = event.detail;

menuTriggerElement.addEventListener(
'click',
() => {
const isMenuAttached = !!parseInt(menuTriggerElement.dataset.isMenuAttached, 10);

if (!isMenuAttached) {
event.preventDefault();

createMenu({
triggerElement: menuTriggerElement,
container: menuContainer,
...menuData,
});
}
},
false,
);
});

emdedItemsUpdateChannel.addEventListener('message', (event) => {
updateNodes(event.data.contentId);
});
})(window, document, window.ibexa, window.Routing, window.Translator, window.Popper);
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,34 @@
const { escapeHTML } = ibexa.helpers.text;
const itemNodes = relationsContainer.querySelectorAll('.ibexa-relations__item');
const itemNode = itemNodes[itemNodes.length - 1];

itemNode.setAttribute('data-content-id', escapeHTML(item.ContentInfo.Content._id));
const contentId = escapeHTML(item.ContentInfo.Content._id);
const locationId = item.id;
const currentVersionNo = item.ContentInfo.Content.CurrentVersion.Version.VersionInfo.versionNo;
const languageCodes = item.ContentInfo.Content.CurrentVersion.Version.VersionInfo.VersionTranslationInfo.Language.map(
(language) => language.languageCode,
);
const itemActionsMenuContainer = relationsContainer.querySelector('.ibexa-embedded-item-actions__menu');
const itemActionsTriggerElement = relationsContainer.querySelector('.ibexa-embedded-item-actions__menu-trigger-btn');
const itemNodeNameCell = itemNode.querySelector('.ibexa-relations__item-name');

itemNode.dataset.contentId = contentId;
itemNode.querySelector('.ibexa-relations__table-action--remove-item').addEventListener('click', removeItem, false);

itemNodeNameCell.dataset.ibexaUpdateContentId = contentId;
itemNodeNameCell.dataset.ibexaUpdateSourceDataPath = 'Content.Name';

doc.body.dispatchEvent(
new CustomEvent('ibexa-embedded-item:create-dynamic-menu', {
detail: {
contentId,
locationId,
languageCodes,
versionNo: currentVersionNo,
menuTriggerElement: itemActionsTriggerElement,
menuContainer: itemActionsMenuContainer,
},
}),
);
});

ibexa.helpers.tooltips.parse();
Expand Down Expand Up @@ -143,7 +168,7 @@
const { formatShortDateTime } = ibexa.helpers.timezone;
const contentTypeName = ibexa.helpers.contentType.getContentTypeName(item.ContentInfo.Content.ContentTypeInfo.identifier);
const contentName = escapeHTML(item.ContentInfo.Content.TranslatedName);
const contentId = item.ContentInfo.Content._id;
const contentId = escapeHTML(item.ContentInfo.Content._id);
const { rowTemplate } = relationsWrapper.dataset;

return rowTemplate
Expand Down
Loading

0 comments on commit ac49197

Please sign in to comment.