diff --git a/src/main/java/org/jahia/modules/contenteditor/api/forms/EditorFormServiceImpl.java b/src/main/java/org/jahia/modules/contenteditor/api/forms/EditorFormServiceImpl.java index 1d1eb644e..a13cecb35 100644 --- a/src/main/java/org/jahia/modules/contenteditor/api/forms/EditorFormServiceImpl.java +++ b/src/main/java/org/jahia/modules/contenteditor/api/forms/EditorFormServiceImpl.java @@ -118,18 +118,18 @@ private Form getEditorForm(ExtendedNodeType primaryNodeType, JCRNodeWrapper exis final SortedSet mergeSet = new TreeSet<>(DefinitionRegistryItemComparator.INSTANCE); // First, primary node type and inherited - addFormNodeType(primaryNodeType, site, mergeSet, locale, false, processedNodeTypes); + addFormNodeType(currentNode, primaryNodeType, site, mergeSet, locale, false, processedNodeTypes); // Available extends mixins List extendMixins = getExtendMixins(primaryNodeType, site); for (ExtendedNodeType extendMixin : extendMixins) { - addFormNodeType(extendMixin, site, mergeSet, locale, true, processedNodeTypes); + addFormNodeType(currentNode, extendMixin, site, mergeSet, locale, true, processedNodeTypes); } // Mixins added on node if (existingNode != null) { for (ExtendedNodeType mixinNodeType : existingNode.getMixinNodeTypes()) { - addFormNodeType(mixinNodeType, site, mergeSet, locale, false, processedNodeTypes); + addFormNodeType(currentNode, mixinNodeType, site, mergeSet, locale, false, processedNodeTypes); } } @@ -253,11 +253,11 @@ private static Map replaceBySubstitutor(Map sele return map; } - private void addFormNodeType(ExtendedNodeType nodeType, JCRSiteNode site, SortedSet formDefinitionsToMerge, Locale locale, boolean singleFieldSet, Set processedNodeTypes) throws RepositoryException { + private void addFormNodeType(JCRNodeWrapper node, ExtendedNodeType nodeType, JCRSiteNode site, SortedSet formDefinitionsToMerge, Locale locale, boolean singleFieldSet, Set processedNodeTypes) throws RepositoryException { if (!processedNodeTypes.contains(nodeType.getName())) { formDefinitionsToMerge.add(FormGenerator.generateForm(nodeType, locale, singleFieldSet)); - formDefinitionsToMerge.addAll(staticDefinitionsRegistry.getFormsForType(nodeType, site)); - formDefinitionsToMerge.addAll(staticDefinitionsRegistry.getFieldSetsForType(nodeType, site)); + formDefinitionsToMerge.addAll(staticDefinitionsRegistry.getFormsForType(node, nodeType, site)); + formDefinitionsToMerge.addAll(staticDefinitionsRegistry.getFieldSetsForType(node, nodeType, site)); processedNodeTypes.add(nodeType.getName()); processedNodeTypes.addAll(nodeType.getSupertypeSet().stream().map(ExtendedNodeType::getName).collect(Collectors.toList())); diff --git a/src/main/java/org/jahia/modules/contenteditor/api/forms/StaticDefinitionsRegistry.java b/src/main/java/org/jahia/modules/contenteditor/api/forms/StaticDefinitionsRegistry.java index 5678aa5c0..93f5aeeaf 100644 --- a/src/main/java/org/jahia/modules/contenteditor/api/forms/StaticDefinitionsRegistry.java +++ b/src/main/java/org/jahia/modules/contenteditor/api/forms/StaticDefinitionsRegistry.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.jahia.modules.contenteditor.api.forms.model.*; import org.jahia.modules.contenteditor.utils.ContentEditorUtils; +import org.jahia.services.content.JCRNodeWrapper; import org.jahia.services.content.decorator.JCRSiteNode; import org.jahia.services.content.nodetypes.ExtendedNodeType; import org.jahia.services.content.nodetypes.NodeTypeRegistry; @@ -39,6 +40,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.jcr.RepositoryException; import javax.jcr.nodetype.NoSuchNodeTypeException; import java.io.IOException; import java.net.URL; @@ -95,11 +97,12 @@ public void bundleChanged(BundleEvent event) { * @param site * @return form definitions that match the type */ - public Collection
getFormsForType(ExtendedNodeType type, JCRSiteNode site) { + public Collection getFormsForType(JCRNodeWrapper node, ExtendedNodeType type, JCRSiteNode site) { return forms.stream() .filter(definition -> definition.getConditionNodeTypeName() != null) .filter(definition -> type.isNodeType(definition.getConditionNodeTypeName()) && (definition.getCondition() == null || matchCondition(definition.getCondition(), type, site))) + .filter(definition -> checkMixinCondition(definition.getCondition(), node)) .collect(Collectors.toCollection(ArrayList::new)); } @@ -109,14 +112,41 @@ public Collection getFormsForType(ExtendedNodeType type, JCRSiteNode site) * @param type to look at * @return form definitions that match the type */ - public Collection
getFieldSetsForType(ExtendedNodeType type, JCRSiteNode site) { + public Collection
getFieldSetsForType(JCRNodeWrapper node, ExtendedNodeType type, JCRSiteNode site) { return fieldSets.stream() .filter(definition -> definition.getConditionNodeTypeName() != null) .filter(definition -> type.isNodeType(definition.getConditionNodeTypeName()) && (definition.getCondition() == null || matchCondition(definition.getCondition(), type, site))) + .filter(definition -> checkMixinCondition(definition.getCondition(), node)) .collect(Collectors.toCollection(ArrayList::new)); } + public boolean checkMixinCondition(Condition condition, JCRNodeWrapper node) { + // step 1 - check if we have condition + if (condition == null) return true; + + String conditionType = condition.getNodeType(); + if (conditionType == null) return true; + + // step 2 - get definition from name + ExtendedNodeType conditionTypeDefinition; + try { + conditionTypeDefinition = NodeTypeRegistry.getInstance().getNodeType(conditionType); + } catch (NoSuchNodeTypeException e) { + return true; + } + + // step 3 - check if the condition node is a mixin + if (!conditionTypeDefinition.isMixin()) return true; + + // step 4 - check if node has the mixin + try { + return Arrays.stream(node.getMixinNodeTypes()).anyMatch(m -> m.getName().equals(conditionType)); + } catch (RepositoryException e) { + return true; + } + } + public boolean matchCondition(Condition condition, ExtendedNodeType type, JCRSiteNode site) { return (condition.getOrderable() == null || type.hasOrderableChildNodes()) && (condition.getWithPermission() == null || site.hasPermission(condition.getWithPermission())) && diff --git a/tests/cypress/e2e/contentEditor/choiceTree.cy.ts b/tests/cypress/e2e/contentEditor/choiceTree.cy.ts index 1d476201d..e11e4daf1 100644 --- a/tests/cypress/e2e/contentEditor/choiceTree.cy.ts +++ b/tests/cypress/e2e/contentEditor/choiceTree.cy.ts @@ -69,10 +69,17 @@ describe('Test Choicetree selector type', () => { }); }); - beforeEach('', () => { + beforeEach('Login and load content folders', () => { + cy.loginAndStoreSession(); jcontent = JContent.visit(siteKey, 'en', 'content-folders/contents'); }); + after('Clean up test env', () => { + deleteSite(siteKey); + deleteNode('/sites/systemsite/categories/choiceTreeCategoryRoot'); + cy.logout(); + }); + it('can handle choice tree selector with a custom root path', () => { // Create content const contentEditor = jcontent.createContent('testChoiceTree'); @@ -126,7 +133,7 @@ describe('Test Choicetree selector type', () => { choiceTree.getEntries().should('contain', 'choiceTreeContent2'); }); - it.only('displays the Choice tree selector for single value', () => { + it.skip('displays the Choice tree selector for single value', () => { const contentEditor = jcontent.createContent('testChoiceTree'); contentEditor.openSection('Content'); const choiceTreeField = contentEditor.getChoiceTreeField('cent:testChoiceTree_singleChoiceTree', false); @@ -157,10 +164,4 @@ describe('Test Choicetree selector type', () => { choiceTree.getEntries().should('contain', 'choiceTreeCategory1'); // Simple check that category selector is displayed properly with expected values. }); - - after('Clean up test env', () => { - cy.loginAndStoreSession(); - deleteSite(siteKey); - deleteNode('/sites/systemsite/categories/choiceTreeCategoryRoot'); - }); }); diff --git a/tests/cypress/e2e/contentEditor/hidePreview.cy.ts b/tests/cypress/e2e/contentEditor/hidePreview.cy.ts index 86138f9b7..f36af1677 100644 --- a/tests/cypress/e2e/contentEditor/hidePreview.cy.ts +++ b/tests/cypress/e2e/contentEditor/hidePreview.cy.ts @@ -1,15 +1,9 @@ import {addNode, createSite, deleteSite, getNodeByPath} from '@jahia/cypress'; import {CategoryManager, JContent} from '../../page-object'; -const siteKey = 'hidePreviewSite'; - -const initVisit = () => { - const jcontent = JContent.visit(siteKey, 'en', 'home.html'); - jcontent.switchToPageBuilder(); - return jcontent; -}; - describe('Hide Preview testsuite', () => { + const siteKey = 'hidePreviewSite'; + before('Create site and content', () => { createSite(siteKey); addNode({ @@ -31,10 +25,10 @@ describe('Hide Preview testsuite', () => { beforeEach('login and visit home', () => { cy.login(); - initVisit(); }); it('Preview shouldn\'t exist for a site', () => { + JContent.visit(siteKey, 'en', 'home.html'); getNodeByPath(`/sites/${siteKey}`).then(res => { cy.visit(`/jahia/administration/digitall/settings/properties#(contentEditor:!((formKey:modal_0,isFullscreen:!t,lang:en,mode:edit,site:${siteKey},uilang:en,uuid:'${res.data.jcr.nodeByPath.uuid}')))`); }); @@ -42,7 +36,7 @@ describe('Hide Preview testsuite', () => { }); it('Preview shouldn\'t exist for a content folder', () => { - const jcontent = initVisit(); + const jcontent = JContent.visit(siteKey, 'en', 'home.html'); jcontent.getAccordionItem('content-folders').click(); const ce = jcontent.editComponentByText('ContentFolder'); ce.switchToAdvancedMode(); @@ -50,7 +44,7 @@ describe('Hide Preview testsuite', () => { }); it('Preview should be visible for a content', () => { - const jcontent = initVisit(); + const jcontent = JContent.visit(siteKey, 'en', 'home.html'); jcontent.getAccordionItem('content-folders').click(); const ce = jcontent.editComponentByText('Text'); ce.switchToAdvancedMode(); diff --git a/tests/cypress/e2e/contentEditor/jsonOverrides/extendMixins.cy.ts b/tests/cypress/e2e/contentEditor/jsonOverrides/extendMixins.cy.ts new file mode 100644 index 000000000..463a967e3 --- /dev/null +++ b/tests/cypress/e2e/contentEditor/jsonOverrides/extendMixins.cy.ts @@ -0,0 +1,43 @@ +import {JContent} from '../../../page-object'; +import {SmallTextField} from '../../../page-object/fields'; +import {createSite, deleteSite, enableModule} from '@jahia/cypress'; + +// This test makes use of jmix_freezeSystemName.json form override from jcontent-test-module +describe('Extend Mixins tests with CE', () => { + const siteKey = 'extendMixinsSite'; + + before(() => { + createSite(siteKey); + enableModule('jcontent-test-module', siteKey); + }); + + after(() => { + cy.logout(); + deleteSite(siteKey); + }); + + beforeEach(() => { + cy.loginAndStoreSession(); + }); + + it('Applies extend mixin only if it is enabled on the node', () => { + const jcontent = JContent.visit(siteKey, 'en', 'pages'); + let contentEditor = jcontent.editPage(); + const systemNameFieldSel = 'nt:base_ce:systemName'; + + cy.log('Verify conditional override is not applied'); + let systemNameField = contentEditor.getField(SmallTextField, systemNameFieldSel); + systemNameField.isNotReadOnly(); + + cy.log('Enable the mixin'); + contentEditor.toggleOption('jmix:freezeSystemName'); + contentEditor.save(); + + cy.log('Verify conditional override is applied after applying mixin'); + contentEditor = jcontent.editPage(); + systemNameField = contentEditor.getField(SmallTextField, systemNameFieldSel); + systemNameField.isReadOnly(); + + contentEditor.cancel(); + }); +}); diff --git a/tests/cypress/e2e/jcontent/search.cy.ts b/tests/cypress/e2e/jcontent/search.cy.ts index 638976b71..bf60860a6 100644 --- a/tests/cypress/e2e/jcontent/search.cy.ts +++ b/tests/cypress/e2e/jcontent/search.cy.ts @@ -87,8 +87,15 @@ describe('Search tests', () => { }); describe('after close', () => { - it('should go back to content-folders after close', () => { + beforeEach(() => { cy.loginAndStoreSession(); + }); + + after(() => { + cy.logout(); + }); + + it('should go back to content-folders after close', () => { jcontent = JContent.visit('jcontentSite', 'en', 'content-folders/contents'); jcontent.getBasicSearch().openSearch().searchTerm('test').executeSearch().close(); jcontent.getAccordionItem('content-folders').getTreeItem('contents').shouldBeSelected(); @@ -96,7 +103,6 @@ describe('Search tests', () => { }); it('should go back to pages after close', () => { - cy.loginAndStoreSession(); jcontent = JContent.visit('jcontentSite', 'en', 'pages/home'); jcontent.getBasicSearch().openSearch().searchTerm('test').executeSearch().close(); jcontent.getAccordionItem('pages').getTreeItem('home').shouldBeSelected(); @@ -104,7 +110,6 @@ describe('Search tests', () => { }); it('should go back to default', () => { - cy.loginAndStoreSession(); cy.visit('/jahia/jcontent/jcontentSite/en/search/sites/jcontentSite/contents?params=(searchContentType:%27%27,searchPath:/sites/digitall/home,searchTerms:test)'); jcontent = new JContent(); new BasicSearch(jcontent).close(); @@ -114,12 +119,16 @@ describe('Search tests', () => { }); describe('advanced search', {testIsolation: false}, () => { - before(() => { + beforeEach(() => { cy.loginAndStoreSession(); - jcontent = JContent.visit('jcontentSite', 'en', 'content-folders/contents'); + }); + + after(() => { + cy.logout(); }); it('should find event by from ', () => { + jcontent = JContent.visit('jcontentSite', 'en', 'content-folders/contents'); jcontent.selectAccordion('pages'); basicSearch = jcontent.getBasicSearch().openSearch().switchToAdvanced() .searchFrom('jnt:event') diff --git a/tests/cypress/e2e/menuActions/areaActions.cy.ts b/tests/cypress/e2e/menuActions/areaActions.cy.ts index 5673de32a..9050be141 100644 --- a/tests/cypress/e2e/menuActions/areaActions.cy.ts +++ b/tests/cypress/e2e/menuActions/areaActions.cy.ts @@ -1,6 +1,6 @@ -import {JContent, JContentPageBuilder} from '../../page-object'; +import {JContent} from '../../page-object'; describe('Area actions', () => { - let jcontent: JContentPageBuilder; + let jcontent: JContent; before(() => { cy.executeGroovy('jcontent/createSite.groovy', {SITEKEY: 'jcontentSite'}); @@ -14,21 +14,20 @@ describe('Area actions', () => { beforeEach(() => { cy.loginAndStoreSession(); - jcontent = JContent - .visit('jcontentSite', 'en', 'pages/home') - .switchToPageBuilder(); + jcontent = JContent.visit('jcontentSite', 'en', 'pages/home'); }); it('Checks that delete, copy and cut menu items are not present on areas in page builder', () => { + const jcontentPageBuilder = jcontent.switchToPageBuilder(); cy.apollo({mutationFile: 'jcontent/createTextContentUnderPath.graphql', variables: { path: '/sites/jcontentSite/home/landing' }}); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(3000); - jcontent.refresh(); + jcontentPageBuilder.refresh(); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(3000); - const menu = jcontent.getModule('/sites/jcontentSite/home/landing') + const menu = jcontentPageBuilder.getModule('/sites/jcontentSite/home/landing') .contextMenu(true); menu.shouldNotHaveItem('Delete'); menu.shouldNotHaveItem('Copy'); @@ -37,14 +36,15 @@ describe('Area actions', () => { }); it('Checks that content can be pasted into the area', () => { - jcontent.getModule('/sites/jcontentSite/home/area-main/test-content1').contextMenu(true).select('Copy'); - jcontent.getModule('/sites/jcontentSite/home/landing') + const jcontentPageBuilder = jcontent.switchToPageBuilder(); + jcontentPageBuilder.getModule('/sites/jcontentSite/home/area-main/test-content1').contextMenu(true).select('Copy'); + jcontentPageBuilder.getModule('/sites/jcontentSite/home/landing') .contextMenu(true, false) .select('Paste'); - jcontent.refresh(); + jcontentPageBuilder.refresh(); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(3000); - jcontent.getModule('/sites/jcontentSite/home/landing/test-content1').should('exist'); + jcontentPageBuilder.getModule('/sites/jcontentSite/home/landing/test-content1').should('exist'); }); it('Checks that delete, copy and cut menu items are not present on areas in structured view', () => { diff --git a/tests/cypress/e2e/pageBuilder/boxesHeader.cy.ts b/tests/cypress/e2e/pageBuilder/boxesHeader.cy.ts index d066a2ef6..f8886e652 100644 --- a/tests/cypress/e2e/pageBuilder/boxesHeader.cy.ts +++ b/tests/cypress/e2e/pageBuilder/boxesHeader.cy.ts @@ -15,17 +15,20 @@ describe('Page builder', () => { beforeEach(() => { cy.loginAndStoreSession(); - jcontent = JContent - .visit('jcontentSite', 'en', 'pages/home') - .switchToPageBuilder(); }); describe('boxes and header', function () { it('should show box when hovering', () => { + jcontent = JContent + .visit('jcontentSite', 'en', 'pages/home') + .switchToPageBuilder(); jcontent.getModule('/sites/jcontentSite/home/landing').hover().should('have.attr', 'data-current', 'true'); }); it('should show box with name, status and edit buttons', () => { + jcontent = JContent + .visit('jcontentSite', 'en', 'pages/home') + .switchToPageBuilder(); jcontent.getModule('/sites/jcontentSite/home/area-main/test-content4').click(); const header = jcontent.getModule('/sites/jcontentSite/home/area-main/test-content4').getHeader(); header.get().find('p').contains('test-content4'); @@ -35,17 +38,26 @@ describe('Page builder', () => { }); it('should trim long titles', () => { + jcontent = JContent + .visit('jcontentSite', 'en', 'pages/home') + .switchToPageBuilder(); jcontent.getModule('/sites/jcontentSite/home/area-main/test-content8-long-text').click(); const header = jcontent.getModule('/sites/jcontentSite/home/area-main/test-content8-long-text').getHeader(); header.get().find('p').contains('Lorem ipsum dolor sit am...'); }); it('should show create buttons', () => { + jcontent = JContent + .visit('jcontentSite', 'en', 'pages/home') + .switchToPageBuilder(); jcontent.getModule('/sites/jcontentSite/home/landing').getCreateButtons().getButton('New content'); }); }); it('Click on links should open modal', () => { + jcontent = JContent + .visit('jcontentSite', 'en', 'pages/home') + .switchToPageBuilder(); jcontent.getSecondaryNav().get().find('[data-sel-role="home"] .moonstone-treeView_itemToggle').click(); cy.contains('external-link').click(); cy.contains('The link redirects to an external URL'); diff --git a/tests/cypress/page-object/contentEditor.ts b/tests/cypress/page-object/contentEditor.ts index 63eeb7f36..8d2c374cb 100644 --- a/tests/cypress/page-object/contentEditor.ts +++ b/tests/cypress/page-object/contentEditor.ts @@ -208,10 +208,12 @@ export class ContentEditor extends BasePage { return r; } - toggleOption(optionType: string, optionFieldName: string) { + toggleOption(optionType: string, optionFieldName?: string) { cy.get(`span[data-sel-role-dynamic-fieldset="${optionType}"]`).scrollIntoView({offset: {left: 0, top: -100}}); cy.get(`span[data-sel-role-dynamic-fieldset="${optionType}"]`).find('input').click({force: true}); - cy.contains(optionFieldName, {timeout: 30000}).should('exist'); + if (optionFieldName) { + cy.contains(optionFieldName, {timeout: 30000}).should('exist'); + } } checkButtonStatus(role: string, enabled: boolean) { diff --git a/tests/cypress/page-object/fields/smallTextField.ts b/tests/cypress/page-object/fields/smallTextField.ts index 21ac66928..c37a95519 100644 --- a/tests/cypress/page-object/fields/smallTextField.ts +++ b/tests/cypress/page-object/fields/smallTextField.ts @@ -43,6 +43,10 @@ export class SmallTextField extends Field { return this.get().find('input').should('have.attr', 'readonly'); } + isNotReadOnly() { + return this.get().find('input').should('not.have.attr', 'readonly'); + } + checkValues(expectedValues: string[]) { if (this.multiple) { this.get().find('input').should($input => { diff --git a/tests/jahia-module/jcontent-test-module/src/main/resources/META-INF/definitions.cnd b/tests/jahia-module/jcontent-test-module/src/main/resources/META-INF/definitions.cnd index 74b57985f..01eaa0286 100644 --- a/tests/jahia-module/jcontent-test-module/src/main/resources/META-INF/definitions.cnd +++ b/tests/jahia-module/jcontent-test-module/src/main/resources/META-INF/definitions.cnd @@ -162,3 +162,6 @@ - singleChoiceTree (weakreference) - multipleChoiceTree (weakreference) multiple - multipleChoiceTreeWithTypes (weakreference) multiple + +[jmix:freezeSystemName] mixin + extends=jnt:page diff --git a/tests/jahia-module/jcontent-test-module/src/main/resources/META-INF/jahia-content-editor-forms/forms/jmix_freezeSystemName.json b/tests/jahia-module/jcontent-test-module/src/main/resources/META-INF/jahia-content-editor-forms/forms/jmix_freezeSystemName.json new file mode 100644 index 000000000..81843a7e6 --- /dev/null +++ b/tests/jahia-module/jcontent-test-module/src/main/resources/META-INF/jahia-content-editor-forms/forms/jmix_freezeSystemName.json @@ -0,0 +1,26 @@ +{ + "nodeType": "jnt:page", + "condition": { + "nodeType": "jmix:freezeSystemName" + }, + "priority": 1.0, + "sections": [ + { + "name": "content", + "fieldSets": [ + { + "name": "
", + "rank": "1.0", + "fields": [ + { + "name": "ce:systemName", + "label": "Customized system name", + "description": "Customized description", + "readOnly" : true + } + ] + } + ] + } + ] +}