Skip to content

Commit

Permalink
QA-15066: add unpublish action on vanity urls (#221)
Browse files Browse the repository at this point in the history
* QA-15066: add unpublish action on vanity urls
* fix publication check failing after moving a url
  • Loading branch information
jsinovassin committed Jul 17, 2024
1 parent 4235518 commit 8a267b5
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 15 deletions.
14 changes: 9 additions & 5 deletions src/javascript/components/Register/registerActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,30 @@ export const registerActions = () => {
});

registry.add('action', 'moveVanity', {
targets: ['site-settings-seo/vanity-list-menu:0.2'],
targets: ['site-settings-seo/vanity-list-menu:0.2', 'site-settings-seo/selected-vanity-list-menu:0.2'],
dataSelRole: 'moveVanity',
buttonIcon: <SwapHoriz/>,
buttonLabel: 'site-settings-seo:label.actions.move',
component: MoveVanityAction
});

registry.add('action', 'publishVanity', {
targets: ['site-settings-seo/vanity-list-menu:0.3'],
targets: ['site-settings-seo/vanity-list-menu:0.3', 'site-settings-seo/selected-vanity-list-menu:0.3'],
buttonIcon: <Publish/>,
buttonLabel: 'site-settings-seo:label.actions.publish',
component: PublishVanityAction
});

registry.addOrReplace('action', 'delete', registry.get('action', 'delete'), {
targets: ['site-settings-seo/vanity-list-menu:0.4']
targets: ['site-settings-seo/vanity-list-menu:0.4', 'site-settings-seo/selected-vanity-list-menu:0.4']
});

registry.addOrReplace('action', 'undelete', registry.get('action', 'undelete'), {
targets: ['site-settings-seo/vanity-list-menu:0.4']
targets: ['site-settings-seo/vanity-list-menu:0.4', 'site-settings-seo/selected-vanity-list-menu:0.4']
});

registry.addOrReplace('action', 'deletePermanently', registry.get('action', 'deletePermanently'), {
targets: ['site-settings-seo/vanity-list-menu:0.5']
targets: ['site-settings-seo/vanity-list-menu:0.5', 'site-settings-seo/selected-vanity-list-menu:0.5']
});

registry.add('action', 'publishAllVanity', {
Expand All @@ -61,4 +61,8 @@ export const registerActions = () => {
buttonProps: {color: 'accent'},
label: 'site-settings-seo:label.actions.publishVanityUrl'
});

registry.addOrReplace('action', 'unpublish', registry.get('action', 'unpublish'), {
targets: ['site-settings-seo/vanity-list-menu:0.6']
});
};
8 changes: 2 additions & 6 deletions src/javascript/components/Toolbar/Toolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,15 @@ export const Toolbar = ({selection, onChangeSelection, actions}) => {
{selection.length &&
<div className={classes.buttonsBar}>
<DisplayActions
target="site-settings-seo/vanity-list-menu"
target="site-settings-seo/selected-vanity-list-menu"
urlPairs={selection}
paths={paths}
actions={actions}
render={ButtonRenderer || LocalButtonRenderer}
filter={action => {
return action.key !== 'updateVanity';
}}
onChangeSelection={onChangeSelection}
onDeleted={onChangeSelection}
/>
</div>
}
</div>}
</Paper>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/javascript/components/Utils/Utils.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const atLeastOneLockedAndCanNotBeEdited = urls => {
};

export const atLeastOneLockedForValidation = urls => {
return urls.some(url => url.default.lockedAndCannotBeEdited && url.default.lockInfo.details.some(detail => detail.type === 'validation'));
return urls?.some(url => url?.default?.lockedAndCannotBeEdited && url.default?.lockInfo.details.some(detail => detail.type === 'validation'));
};

export const atLeastOneCanonicalLockedForLang = (urls, lang) => {
Expand Down
1 change: 1 addition & 0 deletions src/javascript/components/VanityList/DefaultRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const DefaultRow = ({
<DisplayAction
disabled={!hasWritePermission}
path={urlPair.default.path}
language={urlPair.default.language}
urlPair={urlPair}
urlPairs={[urlPair]}
actions={actions}
Expand Down
2 changes: 1 addition & 1 deletion src/javascript/components/actions/PublishAllAction.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const PublishAllAction = ({render: Render, loading: Loading, label, nodeD
};

if (nodeData.urls) {
fetchPublicationData(nodeData.urls.map(url => {
fetchPublicationData(nodeData.urls.filter(url => url.default).map(url => {
return {targetNodePath: url.default.targetNode.path, vanityLanguage: url.default.language};
}));
} else if (data) {
Expand Down
95 changes: 95 additions & 0 deletions src/javascript/components/actions/UnpublishVanityAction.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, {useContext, useEffect, useState} from 'react';
import {ComponentRendererContext} from '@jahia/ui-extender';
import * as PropTypes from 'prop-types';
import {allNotPublishedAndMarkedForDeletion, atLeastOneNotPublished, contentIsVisibleInLive} from '../Utils/Utils';
import {useApolloClient, useQuery} from '@apollo/client';
import {CheckPublishPermissions, GetPublicationStatus} from '~/components/gqlQueries';
import {Publication} from '~/components/Publication/Publication';

export const UnpublishVanityAction = ({render: Render, urlPairs, path, buttonLabel, ...otherProps}) => {
const componentRenderer = useContext(ComponentRendererContext);

const {data, loading, error} = useQuery(CheckPublishPermissions, {variables: {path: path}});

const closeDialog = () => {
componentRenderer.destroy('PublishVanityDialog');
};

const openModal = () => {
componentRenderer.render(
'PublishVanityDialog',
Publication,
{
...otherProps,
urlPairs: urlPairs,
isOpen: true,
onClose: closeDialog
});
};

const openPublicationWorkflow = () => {
const uuids = urlPairs.map(urlPair => urlPair.uuid);
window.authoringApi.openPublicationWorkflow(
uuids,
false, // Not publishing all subNodes (AKA sub pages)
false, // Not publishing all language
false // Not unpublish action
);
};

const [isVisibleInLive, setIsVisibleInLive] = useState(false);
const client = useApolloClient();

useEffect(() => {
const fetchPublicationData = async () => {
const data = await Promise.all(
urlPairs.map(async urlPair => {
const {data} = await client.query({
query: GetPublicationStatus,
variables: {path: urlPair.default.targetNode.path, language: urlPair.default.language}
});
console.debug(data);
return data.jcr.nodeByPath.aggregatedPublicationInfo;
})
);
setIsVisibleInLive(data.every(contentIsVisibleInLive));
};

fetchPublicationData();
}, [client, urlPairs, urlPairs.length]);

if (error) {
console.log('Error while fetching publish permissions');
console.debug(error);
return null;
}

if (loading || !data) {
return null;
}

let requestPublicationLabel = null;
let action = openModal;
if (!data.jcr.nodeByPath.hasPublishPermission && data.jcr.nodeByPath.hasPublicationStartPermission) {
requestPublicationLabel = 'site-settings-seo:label.actions.requestPublication';
action = openPublicationWorkflow;
}

const shouldBeVisible = !allNotPublishedAndMarkedForDeletion(urlPairs) && atLeastOneNotPublished(urlPairs) && isVisibleInLive;
return (
<>
<Render
{...otherProps}
buttonLabel={requestPublicationLabel || buttonLabel}
isVisible={shouldBeVisible}
onClick={action}/>
</>
);
};

UnpublishVanityAction.propTypes = {
render: PropTypes.elementType.isRequired,
urlPairs: PropTypes.array.isRequired,
path: PropTypes.string.isRequired,
buttonLabel: PropTypes.string.isRequired
};
96 changes: 96 additions & 0 deletions tests/cypress/e2e/actions/checkUnpublishActions.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
publishAndWaitJobEnding,
createSite,
deleteSite,
addVanityUrl,
createUser,
deleteUser,
grantRoles,
deleteNode,
unpublishNode,
} from '@jahia/cypress'
import { VanityUrlsPage } from '../../page-object/vanityUrls.page'

describe('Test actions of vanity urls', () => {
const siteKey = 'siteActionCheck'
const sitePath = '/sites/' + siteKey
const homePath = sitePath + '/home'
const pageName = 'basic-page'
const pagePath = homePath + '/' + pageName
const langEN = 'en'
const siteConfig = {
languages: langEN,
templateSet: 'site-settings-seo-test-module',
serverName: 'localhost',
locale: langEN,
}

let pageUuid = ''

const createPage = (parent: string, name: string, template: string, lang: string) => {
return cy.apollo({
variables: {
parentPathOrId: parent,
name: name,
template: template,
language: lang,
},
mutationFile: 'graphql/jcrAddPage.graphql',
})
}

before('Create test data', function () {
createSite(siteKey, siteConfig)

cy.log('Add user to check permissions')
createUser('editorUser', 'password')
grantRoles(sitePath, ['editor'], 'editorUser', 'USER')
})

beforeEach(() => {
createPage(homePath, `${pageName}-a`, 'withoutseo', langEN).then(({ data }) => {
pageUuid = data.jcr.addNode.uuid
addVanityUrl(`${pagePath}-a`, 'en', '/vanityOnPageA')
})
publishAndWaitJobEnding(homePath)
})

afterEach(() => {
deleteNode(`${homePath}/${pageName}-a`)
publishAndWaitJobEnding(homePath)
})

after('Clear test data', function () {
deleteSite(siteKey)
deleteUser('editorUser')
})

it('Should unpublish action enabled in menu for for content existing in live', function () {
cy.login('editorUser', 'password')

const vanityUrlsPage = VanityUrlsPage.visit(siteKey, 'en')
const pageCard = vanityUrlsPage.getPagesWithVanityUrl().getPageCard(pageUuid)
pageCard.open()

const vanityUrlRow = pageCard.getStagingVanityUrls().getVanityUrlRow('/vanityOnPageA')

const menu = vanityUrlRow.openContextualMenu()
menu.getUnpublishButton().invoke('attr', 'aria-disabled').should('eq', 'false')
cy.logout()
})

it('Should unpublish action disabled in menu for for content not existing in live', function () {
cy.login('editorUser', 'password')
unpublishNode(`${homePath}/${pageName}-a`, langEN)

const vanityUrlsPage = VanityUrlsPage.visit(siteKey, 'en')
const pageCard = vanityUrlsPage.getPagesWithVanityUrl().getPageCard(pageUuid)
pageCard.open()

const vanityUrlRow = pageCard.getStagingVanityUrls().getVanityUrlRow('/vanityOnPageA')

const menu = vanityUrlRow.openContextualMenu()
menu.getUnpublishButton().invoke('attr', 'aria-disabled').should('eq', 'true')
cy.logout()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '@jahia/cypress'
import { VanityUrlsPage } from '../../page-object/vanityUrls.page'

describe('Checks the publication action in UIs', () => {
describe('Checks the publication action on not published pages in UIs', () => {
const siteKey = 'testPublishActionsVanity'
const sitePath = '/sites/' + siteKey
const homePath = sitePath + '/home'
Expand Down
2 changes: 1 addition & 1 deletion tests/cypress/e2e/publication/checkUnpublication.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
unpublishNode,
} from '@jahia/cypress'

describe('Checks the publication of the vanity urls', () => {
describe('Checks the unpublication of the vanity urls', () => {
const siteKey = 'testPublishVanityUrls'
const sitePath = '/sites/' + siteKey
const homePath = sitePath + '/home'
Expand Down
4 changes: 4 additions & 0 deletions tests/cypress/page-object/components/Menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ export class Menu extends BaseComponent {
getRequestPublicationButton() {
return this.get().find('[data-sel-role="publishVanity"]').contains('Request publication')
}

getUnpublishButton() {
return this.get().find('[data-sel-role="unpublish"]')
}
}

0 comments on commit 8a267b5

Please sign in to comment.