diff --git a/.changeset/better-chefs-clap.md b/.changeset/better-chefs-clap.md new file mode 100644 index 00000000..97cfb06b --- /dev/null +++ b/.changeset/better-chefs-clap.md @@ -0,0 +1,5 @@ +--- +"strapi-plugin-webtools": patch +--- + +fix: make sure the 'path' parameter from the router endoint doesn't end up on the document service query params diff --git a/.changeset/calm-moments-return.md b/.changeset/calm-moments-return.md new file mode 100644 index 00000000..b32553fb --- /dev/null +++ b/.changeset/calm-moments-return.md @@ -0,0 +1,6 @@ +--- +"webtools-addon-sitemap": minor +"docs": minor +--- + +refactor: replace the auto-generate lifecycle methods with a document service middleware diff --git a/.changeset/floppy-swans-return.md b/.changeset/floppy-swans-return.md new file mode 100644 index 00000000..9f506053 --- /dev/null +++ b/.changeset/floppy-swans-return.md @@ -0,0 +1,6 @@ +--- +"webtools-addon-sitemap": patch +"strapi-plugin-webtools": patch +--- + +fix: make the header button(s) smaller to align with the rest of the admin panel diff --git a/.changeset/lazy-news-fix.md b/.changeset/lazy-news-fix.md new file mode 100644 index 00000000..2c0d58b9 --- /dev/null +++ b/.changeset/lazy-news-fix.md @@ -0,0 +1,5 @@ +--- +"webtools-addon-sitemap": patch +--- + +fix: the 'go to settings' button by making the tab state controlled through URL params diff --git a/.changeset/lazy-sides-fetch.md b/.changeset/lazy-sides-fetch.md new file mode 100644 index 00000000..8fc15b1a --- /dev/null +++ b/.changeset/lazy-sides-fetch.md @@ -0,0 +1,5 @@ +--- +"strapi-plugin-webtools": minor +--- + +feat: enhance the URL alias overview (locale filters & locale and contenttype in the table) diff --git a/.changeset/quiet-beans-stay.md b/.changeset/quiet-beans-stay.md new file mode 100644 index 00000000..e7804383 --- /dev/null +++ b/.changeset/quiet-beans-stay.md @@ -0,0 +1,5 @@ +--- +"strapi-plugin-webtools": patch +--- + +fix: issue with pagination when simulteously using the filter options diff --git a/.changeset/thin-knives-bet.md b/.changeset/thin-knives-bet.md new file mode 100644 index 00000000..620f6e33 --- /dev/null +++ b/.changeset/thin-knives-bet.md @@ -0,0 +1,5 @@ +--- +"webtools-addon-sitemap": patch +--- + +fix: fallback translation for the sitemap index info in the admin panel diff --git a/.changeset/tidy-numbers-fold.md b/.changeset/tidy-numbers-fold.md new file mode 100644 index 00000000..da02c0b6 --- /dev/null +++ b/.changeset/tidy-numbers-fold.md @@ -0,0 +1,5 @@ +--- +"webtools-addon-sitemap": patch +--- + +fix: issue with the lastmod date for localized documents diff --git a/packages/addons/sitemap/admin/components/Header/index.jsx b/packages/addons/sitemap/admin/components/Header/index.jsx index 39d987a2..c4d862e9 100644 --- a/packages/addons/sitemap/admin/components/Header/index.jsx +++ b/packages/addons/sitemap/admin/components/Header/index.jsx @@ -38,7 +38,6 @@ const Header = () => { diff --git a/packages/addons/sitemap/admin/components/Info/index.jsx b/packages/addons/sitemap/admin/components/Info/index.jsx index 868a3332..c10261cf 100644 --- a/packages/addons/sitemap/admin/components/Info/index.jsx +++ b/packages/addons/sitemap/admin/components/Info/index.jsx @@ -3,6 +3,7 @@ import React from 'react'; import { Map } from 'immutable'; import { useIntl } from 'react-intl'; import { useSelector, useDispatch } from 'react-redux'; +import { useSearchParams } from 'react-router-dom'; import { getFetchClient, useNotification } from '@strapi/strapi/admin'; import { @@ -10,6 +11,7 @@ import { Box, Button, Link, + Flex, } from '@strapi/design-system'; import { generateSitemap } from '../../state/actions/Sitemap'; @@ -18,6 +20,7 @@ import { formatTime } from '../../helpers/timeFormat'; const Info = () => { const hasHostname = useSelector((state) => state.getIn(['sitemap', 'initialData', 'hostname'], Map())); const sitemapInfo = useSelector((state) => state.getIn(['sitemap', 'info'], Map())); + let [, setSearchParams] = useSearchParams(); const dispatch = useDispatch(); const { toggleNotification } = useNotification(); const { get } = getFetchClient(); @@ -38,13 +41,13 @@ const Info = () => { {formatMessage({ id: 'sitemap.Info.NoHostname.Title', defaultMessage: 'Set your hostname' })} -
+ {formatMessage({ id: 'sitemap.Info.NoHostname.Description', defaultMessage: 'Before you can generate the sitemap you have to specify the hostname of your website.' })} -
+ ); } @@ -104,7 +107,7 @@ const Info = () => { ) : (
- {formatMessage({ id: 'sitemap.Info.SitemapIsPresent.AmountOfSitemaps', defaultMessage: 'Amount of URLs:' })} + {formatMessage({ id: 'sitemap.Info.SitemapIsPresent.AmountOfSitemaps', defaultMessage: 'Amount of Sitemaps:' })} {sitemapInfo.get('sitemaps')} diff --git a/packages/addons/sitemap/admin/components/ModalForm/Collection/index.jsx b/packages/addons/sitemap/admin/components/ModalForm/Collection/index.jsx index 27a6c5ac..5d430578 100644 --- a/packages/addons/sitemap/admin/components/ModalForm/Collection/index.jsx +++ b/packages/addons/sitemap/admin/components/ModalForm/Collection/index.jsx @@ -83,7 +83,7 @@ const CollectionForm = (props) => { onCheckedChange={(cbValue) => { onChange(uid, langcode, 'includeLastmod', cbValue); }} - value={modifiedState.getIn([uid, 'languages', langcode, 'includeLastmod'], true)} + checked={modifiedState.getIn([uid, 'languages', langcode, 'includeLastmod'], true)} disabled={ !uid || (getSelectedContentType(contentTypes, uid).locales && !langcode) } diff --git a/packages/addons/sitemap/admin/components/Tabs/index.jsx b/packages/addons/sitemap/admin/components/Tabs/index.jsx index 646256c2..c9aff81d 100644 --- a/packages/addons/sitemap/admin/components/Tabs/index.jsx +++ b/packages/addons/sitemap/admin/components/Tabs/index.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { Tabs, Box } from '@strapi/design-system'; import { useIntl } from 'react-intl'; +import { useSearchParams } from 'react-router-dom'; import CollectionURLs from '../../tabs/CollectionURLs'; import CustomURLs from '../../tabs/CustomURLs'; @@ -8,10 +9,17 @@ import Settings from '../../tabs/Settings'; const SitemapTabs = () => { const { formatMessage } = useIntl(); + let [searchParams, setSearchParams] = useSearchParams(); return ( - + { + setSearchParams({ tab: newValue }); + }} + > {formatMessage({ id: 'sitemap.Settings.CollectionTitle', defaultMessage: 'URL bundles' })} {formatMessage({ id: 'sitemap.Settings.CustomTitle', defaultMessage: 'Custom URLs' })} diff --git a/packages/addons/sitemap/server/bootstrap.js b/packages/addons/sitemap/server/bootstrap.js index c50dd548..bb2a9d97 100644 --- a/packages/addons/sitemap/server/bootstrap.js +++ b/packages/addons/sitemap/server/bootstrap.js @@ -37,9 +37,6 @@ export default async () => { } } - // Load lifecycle methods for auto generation of sitemap. - await sitemap.service('lifecycle').loadAllLifecycleMethods(); - // Register permission actions. const actions = [ { diff --git a/packages/addons/sitemap/server/controllers/settings.js b/packages/addons/sitemap/server/controllers/settings.js index afb5502c..bd58364d 100644 --- a/packages/addons/sitemap/server/controllers/settings.js +++ b/packages/addons/sitemap/server/controllers/settings.js @@ -16,9 +16,6 @@ export default { }, updateSettings: async (ctx) => { - const config = await getPluginService('settings').getConfig(); - const newContentTypes = Object.keys(ctx.request.body.contentTypes).filter((x) => !Object.keys(config.contentTypes).includes(x)); - await strapi .store({ environment: '', @@ -27,11 +24,6 @@ export default { }) .set({ key: 'settings', value: ctx.request.body }); - // Load lifecycle methods for auto generation of sitemap. - await newContentTypes.map(async (contentType) => { - await getPluginService('lifecycle').loadLifecycleMethod(contentType); - }); - ctx.send({ ok: true }); }, }; diff --git a/packages/addons/sitemap/server/middlewares/auto-generate.js b/packages/addons/sitemap/server/middlewares/auto-generate.js new file mode 100644 index 00000000..e17b1c7e --- /dev/null +++ b/packages/addons/sitemap/server/middlewares/auto-generate.js @@ -0,0 +1,34 @@ +import { getPluginService } from '../utils/getPluginService'; + +// eslint-disable-next-line max-len +const autoGenerateMiddleware = async (context, next) => { + const { uid, action } = context; + + // Only add the middleware if auto-generate is enabled. + if (!strapi.config.get('plugin::webtools-addon-sitemap.autoGenerate')) { + return next(); + } + + const settings = await getPluginService('settings').getConfig(); + + // Only add the middleware if the content type is added to the sitemap. + if (!settings.contentTypes || !Object.keys(settings.contentTypes).includes(uid)) { + return next(); + } + + // Only add the middleware for the create, update and delete action. + if (!['create', 'update', 'delete'].includes(action)) { + return next(); + } + + // Perform the action. + const document = await next(); + + // Generate the sitemap. + getPluginService('core').createSitemap(); + + // Return the document + return document; +}; + +export default autoGenerateMiddleware; diff --git a/packages/addons/sitemap/server/register.js b/packages/addons/sitemap/server/register.js index e1f45723..0c83c813 100644 --- a/packages/addons/sitemap/server/register.js +++ b/packages/addons/sitemap/server/register.js @@ -3,6 +3,7 @@ import _ from 'lodash'; import { isContentTypeEnabled } from './utils/enabledContentTypes'; +import autoGenerateMiddleware from './middlewares/auto-generate'; /** * Adds sitemap_exclude field to all the eligable content types. @@ -30,5 +31,6 @@ const extendContentTypesWithExcludeField = async (strapi) => { }; export default ({ strapi }) => { + strapi.documents.use(autoGenerateMiddleware); extendContentTypesWithExcludeField(strapi); }; diff --git a/packages/addons/sitemap/server/services/index.js b/packages/addons/sitemap/server/services/index.js index 21da43ca..a0253ff1 100644 --- a/packages/addons/sitemap/server/services/index.js +++ b/packages/addons/sitemap/server/services/index.js @@ -3,11 +3,9 @@ import query from './query'; import core from './core'; import settings from './settings'; -import lifecycle from './lifecycle'; export default { query, core, settings, - lifecycle, }; diff --git a/packages/addons/sitemap/server/services/lifecycle.js b/packages/addons/sitemap/server/services/lifecycle.js deleted file mode 100644 index e441d4a3..00000000 --- a/packages/addons/sitemap/server/services/lifecycle.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -import { logMessage } from '../utils'; -import { getPluginService } from '../utils/getPluginService'; - -/** - * Gets lifecycle service - * - * @returns {object} - Lifecycle service - */ - -const subscribeLifecycleMethods = async (modelName) => { - if (strapi.contentTypes[modelName]) { - await strapi.db.lifecycles.subscribe({ - models: [modelName], - - async afterCreate() { - await getPluginService('core').createSitemap(); - }, - - async afterCreateMany() { - await getPluginService('core').createSitemap(); - }, - - async afterUpdate() { - await getPluginService('core').createSitemap(); - }, - - async afterUpdateMany() { - await getPluginService('core').createSitemap(); - }, - - async afterDelete() { - await getPluginService('core').createSitemap(); - }, - - async afterDeleteMany() { - await getPluginService('core').createSitemap(); - }, - }); - } else { - strapi.log.error(logMessage(`Could not load lifecycles on model '${modelName}'`)); - } -}; - -export default () => ({ - async loadAllLifecycleMethods() { - const settings = await getPluginService('settings').getConfig(); - - // Loop over configured contentTypes from store. - if (settings.contentTypes && strapi.config.get('plugin::webtools-addon-sitemap.autoGenerate')) { - Object.keys(settings.contentTypes).map(async (contentType) => { - await subscribeLifecycleMethods(contentType); - }); - } - }, - - async loadLifecycleMethod(modelName) { - if (strapi.config.get('plugin::webtools-addon-sitemap.autoGenerate')) { - await subscribeLifecycleMethods(modelName); - } - }, -}); diff --git a/packages/addons/sitemap/server/services/query.js b/packages/addons/sitemap/server/services/query.js index 261fecc2..1e008ace 100644 --- a/packages/addons/sitemap/server/services/query.js +++ b/packages/addons/sitemap/server/services/query.js @@ -47,13 +47,13 @@ const getPages = async (config, contentType, ids) => { } : {}, }, ...(isLocalized ? { locale: locale.code } : {}), - fields: isLocalized ? 'locale' : undefined, + fields: isLocalized ? ['locale', 'updatedAt'] : ['updatedAt'], populate: { url_alias: { populate: '*', }, localizations: { - fields: isLocalized ? 'locale' : undefined, + fields: isLocalized ? ['locale', 'updatedAt'] : ['updatedAt'], filters: { $or: [ { diff --git a/packages/core/admin/hooks/useQueryParams.ts b/packages/core/admin/hooks/useQueryParams.ts index 8a240180..ac63ec38 100644 --- a/packages/core/admin/hooks/useQueryParams.ts +++ b/packages/core/admin/hooks/useQueryParams.ts @@ -9,22 +9,24 @@ const useQueryParams = () => { const searchParams = new URLSearchParams(location.search); const page = searchParams.get('page'); const pageSize = searchParams.get('pageSize'); + searchParams.delete('page'); + searchParams.delete('pageSize'); - if (page || pageSize) { - if (page) { - searchParams.delete('page'); - searchParams.append('pagination[page]', page); - } - if (pageSize) { - searchParams.delete('pageSize'); - searchParams.append('pagination[pageSize]', pageSize); - } + if (!page && !pageSize) { + searchParams.append('pagination[page]', '1'); + searchParams.append('pagination[pageSize]', '10'); + } - setParams(searchParams.toString()); + if (page && pageSize) { + searchParams.append('pagination[page]', page); + searchParams.append('pagination[pageSize]', pageSize); } + + setParams(searchParams.toString()); }, [location]); + return params; }; diff --git a/packages/core/admin/screens/List/components/Filters/index.tsx b/packages/core/admin/screens/List/components/Filters/index.tsx index 634c1b03..80625892 100644 --- a/packages/core/admin/screens/List/components/Filters/index.tsx +++ b/packages/core/admin/screens/List/components/Filters/index.tsx @@ -9,13 +9,15 @@ import { import { Filters as StrapiFilters, SearchInput } from '@strapi/strapi/admin'; import FilterInput from './FilterInput'; -import { EnabledContentType, EnabledContentTypes } from '../../../../types/enabled-contenttypes'; +import { EnabledContentTypes } from '../../../../types/enabled-contenttypes'; +import { Locales } from '../../../../types/languages'; type Props = { contentTypes: EnabledContentTypes, + locales: Locales, }; -const Filters = ({ contentTypes }: Props) => { +const Filters = ({ contentTypes, locales }: Props) => { const { formatMessage } = useIntl(); const filters = useMemo(() => { @@ -27,17 +29,27 @@ const Filters = ({ contentTypes }: Props) => { input: FilterInput, label: 'Content-Type', name: 'contenttype', - options: contentTypes.map((contenttype: EnabledContentType) => ({ + options: contentTypes.map((contenttype) => ({ label: contenttype.name, value: contenttype.uid, })), type: 'string', }, + { + input: FilterInput, + label: 'Locale', + name: 'locale', + options: locales.map((locale) => ({ + label: locale.name, + value: locale.uid, + })), + type: 'string', + }, ); } return newFilters; - }, [contentTypes]); + }, [contentTypes, locales]); return ( diff --git a/packages/core/admin/screens/List/components/Table/index.tsx b/packages/core/admin/screens/List/components/Table/index.tsx index 04837e80..101839d7 100644 --- a/packages/core/admin/screens/List/components/Table/index.tsx +++ b/packages/core/admin/screens/List/components/Table/index.tsx @@ -23,12 +23,14 @@ import type { Pagination } from '../..'; import Filters from '../Filters'; import { Config } from '../../../../../server/config'; import { UrlAliasEntity } from '../../../../types/url-aliases'; +import { Locales } from '../../../../types/languages'; type Props = { paths: UrlAliasEntity[], onDelete: () => any, pagination: Pagination, contentTypes: any[], + locales: Locales, config: Config, }; @@ -39,6 +41,7 @@ const TableComponent: FC = (props) => { onDelete, config, contentTypes, + locales, } = props; const { formatMessage } = useIntl(); @@ -60,7 +63,7 @@ const TableComponent: FC = (props) => { return (
- + {amountChecked > 0 && ( @@ -93,7 +96,17 @@ const TableComponent: FC = (props) => { */} - {formatMessage({ id: 'webtools.settings.page.patterns.table.head.label', defaultMessage: 'Path' })} + {formatMessage({ id: 'webtools.settings.page.path.table.head.path', defaultMessage: 'Path' })} + + + + + {formatMessage({ id: 'webtools.settings.page.path.table.head.content-type', defaultMessage: 'Content-Type' })} + + + + + {formatMessage({ id: 'webtools.settings.page.path.table.head.locale', defaultMessage: 'Locale' })} diff --git a/packages/core/admin/screens/List/components/TableRow/index.tsx b/packages/core/admin/screens/List/components/TableRow/index.tsx index b87ac886..2d118959 100644 --- a/packages/core/admin/screens/List/components/TableRow/index.tsx +++ b/packages/core/admin/screens/List/components/TableRow/index.tsx @@ -71,6 +71,16 @@ const TableRow: FC = ({ {row.url_path} + + + {row.contenttype} + + + + + {row.locale} + + {config.website_url && ( diff --git a/packages/core/admin/screens/List/index.tsx b/packages/core/admin/screens/List/index.tsx index e11abe36..ebc94588 100644 --- a/packages/core/admin/screens/List/index.tsx +++ b/packages/core/admin/screens/List/index.tsx @@ -24,6 +24,7 @@ import { GenericResponse } from '../../types/content-api'; import { Config } from '../../../server/config'; import { UrlAliasEntity } from '../../types/url-aliases'; import useQueryParams from '../../hooks/useQueryParams'; +import { Locales } from '../../types/languages'; export type Pagination = { page: number; @@ -38,6 +39,7 @@ const List = () => { const items = useQuery(['url-alias', params], async () => get>(`/webtools/url-alias/findMany?${params}`)); const contentTypes = useQuery('content-types', async () => get('/webtools/info/getContentTypes')); + const locales = useQuery('languages', async () => get('/webtools/info/getLanguages')); const config = useQuery('config', async () => get('/webtools/info/config')); const queryClient = useQueryClient(); @@ -58,13 +60,13 @@ const List = () => { await queryClient.invalidateQueries('url-alias'); }; - if (items.isLoading || config.isLoading || contentTypes.isLoading) { + if (items.isLoading || config.isLoading || contentTypes.isLoading || locales.isLoading) { return ( ); } - if (items.isError || config.isError || contentTypes.isError) { + if (items.isError || config.isError || contentTypes.isError || locales.isError) { return (
error
); @@ -72,7 +74,6 @@ const List = () => { return ( - {false && } { onSubmit={handleGeneratePaths} contentTypes={contentTypes.data.data} > -