├── .nvmrc ├── playground ├── src │ ├── api │ │ ├── .gitkeep │ │ ├── home │ │ │ ├── routes │ │ │ │ └── home.ts │ │ │ ├── services │ │ │ │ └── home.ts │ │ │ ├── controllers │ │ │ │ └── home.ts │ │ │ └── content-types │ │ │ │ └── home │ │ │ │ └── schema.json │ │ ├── category │ │ │ ├── routes │ │ │ │ └── category.ts │ │ │ ├── services │ │ │ │ └── category.ts │ │ │ ├── controllers │ │ │ │ └── category.ts │ │ │ └── content-types │ │ │ │ └── category │ │ │ │ └── schema.json │ │ ├── test │ │ │ ├── routes │ │ │ │ └── test.ts │ │ │ ├── services │ │ │ │ └── test.ts │ │ │ └── controllers │ │ │ │ └── test.ts │ │ └── private-category │ │ │ ├── routes │ │ │ └── private-category.ts │ │ │ ├── services │ │ │ └── private-category.ts │ │ │ ├── controllers │ │ │ └── private-category.ts │ │ │ └── content-types │ │ │ └── private-category │ │ │ └── schema.json │ ├── extensions │ │ └── .gitkeep │ ├── components │ │ └── core │ │ │ └── header.json │ └── admin │ │ ├── vite.config.ts │ │ ├── tsconfig.json │ │ └── app.example.tsx ├── public │ ├── uploads │ │ └── .gitkeep │ └── robots.txt ├── database │ └── migrations │ │ └── .gitkeep ├── favicon.png ├── config │ ├── api.ts │ ├── server.ts │ ├── middlewares.ts │ ├── plugins.ts │ ├── database.ts │ ├── admin.ts │ └── env │ │ └── test │ │ └── database.ts ├── .env ├── .env.example ├── tests │ ├── healthcheck.test.js │ └── helpers.ts ├── types │ └── generated │ │ └── components.d.ts ├── tsconfig.json └── package.json ├── packages ├── docs │ ├── static │ │ ├── .nojekyll │ │ └── img │ │ │ ├── logo.png │ │ │ ├── favicon.jpg │ │ │ ├── assets │ │ │ ├── logo.png │ │ │ ├── url-alias-dark.png │ │ │ ├── url-alias-light.png │ │ │ ├── url_patterns-dark.png │ │ │ ├── add_new_pattern-dark.png │ │ │ ├── admin-url-overview.png │ │ │ ├── enable-webtools-dark.png │ │ │ ├── enable-webtools-light.png │ │ │ ├── install │ │ │ │ ├── cli-select-addons.png │ │ │ │ └── cli-select-content-types.png │ │ │ ├── permissions-ct-find-dark.png │ │ │ ├── addons │ │ │ │ ├── links │ │ │ │ │ ├── ckeditor-light.png │ │ │ │ │ ├── link-component-light.png │ │ │ │ │ ├── link-custom-field-light.png │ │ │ │ │ ├── ckeditor-link-modal-light.png │ │ │ │ │ └── webtools-pro-link-addon-options-light.png │ │ │ │ ├── sitemap │ │ │ │ │ ├── settings-light.png │ │ │ │ │ ├── URL-bundle-light.png │ │ │ │ │ ├── custom-url-light.png │ │ │ │ │ └── multiple-sitemaps.png │ │ │ │ └── redirects │ │ │ │ │ ├── create-redirects-light.png │ │ │ │ │ └── redirects-overview-light.png │ │ │ ├── permissions-admin-roles-dark.png │ │ │ ├── permissions-webtools-find-dark.png │ │ │ └── icons │ │ │ │ ├── after.svg │ │ │ │ ├── back.svg │ │ │ │ ├── provider.svg │ │ │ │ ├── chevron-left.svg │ │ │ │ ├── chevron-right.svg │ │ │ │ ├── minus.svg │ │ │ │ ├── up.svg │ │ │ │ ├── down.svg │ │ │ │ ├── code.svg │ │ │ │ ├── content_manager.svg │ │ │ │ ├── Clock.svg │ │ │ │ ├── clear.svg │ │ │ │ ├── close-icon.svg │ │ │ │ ├── list_view.svg │ │ │ │ ├── crop.svg │ │ │ │ ├── CheckCircle.svg │ │ │ │ ├── edit.svg │ │ │ │ ├── documentation-plugin.svg │ │ │ │ ├── check_icon.svg │ │ │ │ ├── move.svg │ │ │ │ ├── email_template.svg │ │ │ │ ├── add.svg │ │ │ │ ├── ONHOLDCarretDown.svg │ │ │ │ ├── ONHOLDCarretUp.svg │ │ │ │ ├── add_icon.svg │ │ │ │ ├── releases.svg │ │ │ │ ├── carret.svg │ │ │ │ ├── grid_view.svg │ │ │ │ ├── delete.svg │ │ │ │ ├── verified-marketplace.svg │ │ │ │ ├── down2.svg │ │ │ │ ├── application.svg │ │ │ │ ├── up2.svg │ │ │ │ ├── notifications.svg │ │ │ │ ├── download.svg │ │ │ │ ├── add_circle.svg │ │ │ │ ├── webhooks.svg │ │ │ │ ├── ctb_relation_oneway.svg │ │ │ │ ├── documentation.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── ctb_media.svg │ │ │ │ ├── search.svg │ │ │ │ ├── CrossCircle.svg │ │ │ │ ├── duplicate.svg │ │ │ │ ├── users.svg │ │ │ │ ├── Duplicate copy.svg │ │ │ │ ├── more.svg │ │ │ │ ├── ctb_boolean.svg │ │ │ │ ├── official-market.svg │ │ │ │ ├── ctb_text.svg │ │ │ │ ├── external_link.svg │ │ │ │ ├── image.svg │ │ │ │ ├── ctb_date.svg │ │ │ │ ├── cross.svg │ │ │ │ ├── api_tokens.svg │ │ │ │ ├── ctb_password.svg │ │ │ │ ├── roles_permissions.svg │ │ │ │ ├── lock.svg │ │ │ │ ├── media_library.svg │ │ │ │ ├── roles.svg │ │ │ │ ├── Browsers.svg │ │ │ │ ├── plugins.svg │ │ │ │ ├── ctb_relation_1to1.svg │ │ │ │ ├── content_types_builder.svg │ │ │ │ ├── ctb_dz.svg │ │ │ │ ├── CreditCard.svg │ │ │ │ ├── ctb_relation_manyway.svg │ │ │ │ ├── marketplace.svg │ │ │ │ └── MapTrifold.svg │ │ │ └── docusaurus-social-card.jpg │ ├── .yarn │ │ └── install-state.gz │ ├── babel.config.js │ ├── src │ │ ├── scss │ │ │ ├── container.scss │ │ │ ├── scene.scss │ │ │ ├── images.scss │ │ │ ├── pagination-nav.scss │ │ │ ├── _fonts.scss │ │ │ ├── table.scss │ │ │ ├── columns.scss │ │ │ ├── medium-zoom.scss │ │ │ ├── grid.scss │ │ │ ├── footer.scss │ │ │ ├── markdown.scss │ │ │ ├── search.scss │ │ │ ├── custom-doc-cards.scss │ │ │ ├── tabs.scss │ │ │ ├── _mixins.scss │ │ │ ├── __index.scss │ │ │ ├── details.scss │ │ │ ├── card.scss │ │ │ └── typography.scss │ │ └── components │ │ │ ├── HomepageFeatures │ │ │ └── styles.module.css │ │ │ ├── CustomDocCardsWrapper.js │ │ │ ├── index.js │ │ │ ├── Container │ │ │ ├── Container.jsx │ │ │ └── container.module.scss │ │ │ ├── Request.js │ │ │ ├── Response.js │ │ │ ├── ApiCall.js │ │ │ ├── SubtleCallout.js │ │ │ ├── Hero │ │ │ └── Hero.jsx │ │ │ ├── LinkWithArrow │ │ │ └── LinkWithArrow.jsx │ │ │ ├── Button │ │ │ └── Button.jsx │ │ │ └── CustomDocCard.js │ ├── blog │ │ ├── 2021-08-26-welcome │ │ │ ├── docusaurus-plushie-banner.jpeg │ │ │ └── index.md │ │ ├── 2019-05-28-first-blog-post.md │ │ ├── tags.yml │ │ ├── 2021-08-01-mdx-blog-post.mdx │ │ └── authors.yml │ ├── tsconfig.json │ ├── .gitignore │ ├── docs │ │ ├── cli │ │ │ ├── enable.md │ │ │ └── install.md │ │ ├── api │ │ │ └── graphql.md │ │ ├── addons │ │ │ ├── sitemap │ │ │ │ ├── settings │ │ │ │ │ ├── include-homepage.md │ │ │ │ │ ├── language-filter.md │ │ │ │ │ ├── exclude-drafts.md │ │ │ │ │ ├── introduction.md │ │ │ │ │ ├── hostname.md │ │ │ │ │ ├── hostname-overrides.md │ │ │ │ │ └── default-language-url-type.md │ │ │ │ ├── configuration │ │ │ │ │ ├── cron.md │ │ │ │ │ ├── limit.md │ │ │ │ │ ├── introduction.md │ │ │ │ │ ├── xsl.md │ │ │ │ │ └── auto-generate.md │ │ │ │ └── getting-started │ │ │ │ │ ├── robots-txt.md │ │ │ │ │ ├── multilingual.md │ │ │ │ │ ├── sitemap-index.md │ │ │ │ │ └── cli.md │ │ │ ├── redirects │ │ │ │ ├── configuration │ │ │ │ │ ├── default-status-code.md │ │ │ │ │ └── introduction.md │ │ │ │ └── getting-started │ │ │ │ │ └── introduction.md │ │ │ └── links │ │ │ │ ├── api │ │ │ │ └── links-format.md │ │ │ │ └── getting-started │ │ │ │ ├── introduction.md │ │ │ │ └── ckeditor.md │ │ ├── configuration │ │ │ ├── unique-per-locale.md │ │ │ ├── website-url.md │ │ │ ├── default-pattern.md │ │ │ └── slugify.md │ │ └── getting-started │ │ │ └── url-alias.md │ ├── README.md │ └── Dockerfile ├── core │ ├── types │ ├── strapi-server.js │ ├── server │ │ ├── types │ │ │ ├── index.ts │ │ │ └── koa.ts │ │ ├── util │ │ │ ├── isObject.ts │ │ │ ├── pluginId.ts │ │ │ ├── sanitizeOutput.ts │ │ │ ├── enabledContentTypes.ts │ │ │ ├── getPluginService.ts │ │ │ ├── getAddons.ts │ │ │ └── typeHelpers.ts │ │ ├── middlewares │ │ │ ├── index.ts │ │ │ └── prevent-duplicate-urls.ts │ │ ├── content-types │ │ │ ├── index.ts │ │ │ ├── url-pattern │ │ │ │ └── schema.json │ │ │ └── url-alias │ │ │ │ └── schema.json │ │ ├── services │ │ │ ├── index.ts │ │ │ └── get-main-field.ts │ │ ├── index.ts │ │ ├── controllers │ │ │ └── index.ts │ │ ├── config.ts │ │ └── bootstrap.ts │ ├── admin │ │ ├── types │ │ │ ├── theme.ts │ │ │ ├── error-response.ts │ │ │ ├── languages.ts │ │ │ ├── injection-zones.ts │ │ │ ├── url-aliases.ts │ │ │ ├── enabled-contenttypes.ts │ │ │ ├── addons.ts │ │ │ ├── url-patterns.ts │ │ │ └── content-api.ts │ │ ├── helpers │ │ │ ├── getTrad.ts │ │ │ ├── pluginId.ts │ │ │ ├── prefixPluginTranslations.js │ │ │ ├── useActiveElement.ts │ │ │ └── displayedFilters.ts │ │ ├── translations │ │ │ └── index.ts │ │ ├── index.cy.tsx │ │ ├── components │ │ │ ├── PluginIcon │ │ │ │ └── index.tsx │ │ │ ├── Center │ │ │ │ └── index.tsx │ │ │ ├── Loader │ │ │ │ └── index.tsx │ │ │ ├── HiddenLocalizedField │ │ │ │ └── index.tsx │ │ │ └── WebtoolsPanel │ │ │ │ └── Permalink │ │ │ │ └── index.tsx │ │ ├── screens │ │ │ ├── 404 │ │ │ │ └── index.tsx │ │ │ ├── Patterns │ │ │ │ ├── EditPage │ │ │ │ │ └── utils │ │ │ │ │ │ └── schema.ts │ │ │ │ └── CreatePage │ │ │ │ │ └── utils │ │ │ │ │ └── schema.ts │ │ │ └── List │ │ │ │ └── components │ │ │ │ ├── PaginationFooter │ │ │ │ └── index.tsx │ │ │ │ └── Filters │ │ │ │ └── FilterInput │ │ │ │ └── index.tsx │ │ └── permissions.ts │ ├── .npmignore │ ├── .eslintignore │ ├── .gitignore │ ├── packup.config.ts │ └── LICENSE.md ├── addons │ └── sitemap │ │ ├── admin │ │ ├── translations │ │ │ ├── ar.json │ │ │ ├── it.json │ │ │ ├── ko.json │ │ │ ├── pt.json │ │ │ ├── vi.json │ │ │ ├── pl.json │ │ │ ├── pt-BR.json │ │ │ ├── ru.json │ │ │ ├── zh.json │ │ │ ├── zh-Hans.json │ │ │ └── index.js │ │ ├── helpers │ │ │ ├── getTrad.js │ │ │ ├── getTranslation.js │ │ │ ├── pluginId.js │ │ │ ├── getSelectedContentType.js │ │ │ ├── prefixPluginTranslations.js │ │ │ ├── useActiveElement.js │ │ │ └── timeFormat.js │ │ ├── state │ │ │ └── reducers │ │ │ │ └── index.js │ │ ├── components │ │ │ ├── PluginIcon │ │ │ │ └── index.jsx │ │ │ ├── ModalForm │ │ │ │ └── mapper.js │ │ │ └── Loader │ │ │ │ └── index.jsx │ │ ├── screens │ │ │ ├── Overview │ │ │ │ └── index.jsx │ │ │ └── Settings │ │ │ │ └── index.jsx │ │ ├── permissions.js │ │ └── containers │ │ │ └── Providers │ │ │ └── index.jsx │ │ ├── types │ │ ├── strapi-server.js │ │ ├── bin │ │ └── strapi-sitemap │ │ ├── .npmignore │ │ ├── server │ │ ├── content-types │ │ │ ├── index.js │ │ │ └── sitemap │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── index.js │ │ ├── config.js │ │ ├── routes │ │ │ ├── index.js │ │ │ └── admin.js │ │ ├── services │ │ │ └── index.js │ │ ├── utils │ │ │ ├── pluginId.ts │ │ │ ├── enabledContentTypes.ts │ │ │ ├── __tests__ │ │ │ │ └── index.test.js │ │ │ ├── getPluginService.ts │ │ │ └── index.js │ │ ├── migrations │ │ │ └── migrate-config-object-to-array.js │ │ ├── index.js │ │ ├── register.js │ │ └── middlewares │ │ │ └── auto-generate.js │ │ ├── .eslintignore │ │ ├── .gitignore │ │ ├── xsl │ │ ├── sortable.min.js │ │ └── sitemap.xsl.js │ │ ├── packup.config.ts │ │ └── LICENSE.md └── cli │ ├── .eslintignore │ ├── src │ ├── utils │ │ ├── addons.ts │ │ └── strapi.ts │ ├── commands │ │ ├── license-setup.ts │ │ └── enable.ts │ └── constants │ │ └── addons.ts │ ├── .gitignore │ ├── tsconfig.json │ └── LICENSE.md ├── babel.config.js ├── codecov.yml ├── .yarnrc.yml ├── .github ├── url-alias.png ├── url-pattern.png ├── enable-webtools.png ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── release.yml ├── tsconfig.jest.json ├── .editorconfig ├── docker-compose.yml ├── tsconfig.cypress.json ├── turbo.json ├── cypress.config.js ├── .changeset ├── README.md └── config.json ├── jest.config.js ├── .gitignore ├── cypress └── support │ ├── e2e.ts │ └── commands.ts ├── tsconfig.json └── LICENSE.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /playground/src/api/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/docs/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playground/public/uploads/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playground/src/extensions/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/core/types: -------------------------------------------------------------------------------- 1 | ../../playground/types/ -------------------------------------------------------------------------------- /playground/database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/ar.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/it.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/ko.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/pt.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/vi.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/pl.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/pt-BR.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/ru.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/zh.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/addons/sitemap/types: -------------------------------------------------------------------------------- 1 | ../../../playground/types/ -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/zh-Hans.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { presets: ['@babel/preset-env'] }; 2 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | branches: 3 | - master 4 | - develop 5 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.11.0.cjs 4 | -------------------------------------------------------------------------------- /.github/url-alias.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/.github/url-alias.png -------------------------------------------------------------------------------- /packages/core/strapi-server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./dist/server'); 4 | -------------------------------------------------------------------------------- /.github/url-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/.github/url-pattern.png -------------------------------------------------------------------------------- /playground/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/playground/favicon.png -------------------------------------------------------------------------------- /packages/addons/sitemap/strapi-server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./dist/server'); 4 | -------------------------------------------------------------------------------- /.github/enable-webtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/.github/enable-webtools.png -------------------------------------------------------------------------------- /packages/core/server/types/index.ts: -------------------------------------------------------------------------------- 1 | export type GenerationType = 'only_without_alias' | 'only_generated' | 'all'; 2 | -------------------------------------------------------------------------------- /packages/addons/sitemap/bin/strapi-sitemap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | require('../dist/cli'); 6 | -------------------------------------------------------------------------------- /packages/core/admin/types/theme.ts: -------------------------------------------------------------------------------- 1 | export interface Theme { 2 | colors: { 3 | primary100: string, 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | # ignore the .ts and .tsx files 2 | *.ts 3 | *.tsx 4 | 5 | # include the .d.ts files 6 | !*.d.ts 7 | -------------------------------------------------------------------------------- /packages/docs/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/logo.png -------------------------------------------------------------------------------- /packages/docs/.yarn/install-state.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/.yarn/install-state.gz -------------------------------------------------------------------------------- /packages/docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/docs/src/scss/container.scss: -------------------------------------------------------------------------------- 1 | /** Component: Container */ 2 | 3 | .container { 4 | padding-top: 0 !important; 5 | } 6 | -------------------------------------------------------------------------------- /packages/docs/static/img/favicon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/favicon.jpg -------------------------------------------------------------------------------- /packages/addons/sitemap/.npmignore: -------------------------------------------------------------------------------- 1 | # ignore the .ts and .tsx files 2 | *.ts 3 | *.tsx 4 | 5 | # include the .d.ts files 6 | !*.d.ts 7 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/logo.png -------------------------------------------------------------------------------- /playground/config/api.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | rest: { 3 | defaultLimit: 25, 4 | maxLimit: 100, 5 | withCount: true, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/admin/types/error-response.ts: -------------------------------------------------------------------------------- 1 | export interface ErrorResponse { 2 | response: { 3 | payload: Array<{ message: string }>; 4 | }; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/admin/types/languages.ts: -------------------------------------------------------------------------------- 1 | export interface Locale { 2 | name: string, 3 | uid: string, 4 | } 5 | 6 | export type Locales = Locale[]; 7 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/url-alias-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/url-alias-dark.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/url-alias-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/url-alias-light.png -------------------------------------------------------------------------------- /packages/docs/static/img/docusaurus-social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/docusaurus-social-card.jpg -------------------------------------------------------------------------------- /playground/public/robots.txt: -------------------------------------------------------------------------------- 1 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 2 | # User-Agent: * 3 | # Disallow: / 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/url_patterns-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/url_patterns-dark.png -------------------------------------------------------------------------------- /packages/core/admin/helpers/getTrad.ts: -------------------------------------------------------------------------------- 1 | import pluginId from './pluginId'; 2 | 3 | const getTrad = (id: string) => `${pluginId}.${id}`; 4 | 5 | export default getTrad; 6 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/add_new_pattern-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/add_new_pattern-dark.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/admin-url-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/admin-url-overview.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/enable-webtools-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/enable-webtools-dark.png -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/helpers/getTrad.js: -------------------------------------------------------------------------------- 1 | import pluginId from './pluginId'; 2 | 3 | const getTrad = (id) => `${pluginId}.${id}`; 4 | 5 | export default getTrad; 6 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/enable-webtools-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/enable-webtools-light.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/install/cli-select-addons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/install/cli-select-addons.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/permissions-ct-find-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/permissions-ct-find-dark.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/links/ckeditor-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/links/ckeditor-light.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/sitemap/settings-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/sitemap/settings-light.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/permissions-admin-roles-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/permissions-admin-roles-dark.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/permissions-webtools-find-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/permissions-webtools-find-dark.png -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/helpers/getTranslation.js: -------------------------------------------------------------------------------- 1 | import PLUGIN_ID from './pluginId'; 2 | 3 | const getTranslation = (id) => `${PLUGIN_ID}.${id}`; 4 | 5 | export default getTranslation; 6 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/content-types/index.js: -------------------------------------------------------------------------------- 1 | import sitemapSchema from './sitemap/schema.json'; 2 | 3 | export default { 4 | sitemap: { 5 | schema: sitemapSchema, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/admin/translations/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import nl from './nl.json'; 3 | 4 | const trads = { 5 | en, 6 | nl, 7 | }; 8 | 9 | export default trads; 10 | -------------------------------------------------------------------------------- /packages/core/admin/types/injection-zones.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export type InjectedRoute = { 4 | path: string, 5 | label: string, 6 | Component: React.ElementType 7 | }; 8 | -------------------------------------------------------------------------------- /packages/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg -------------------------------------------------------------------------------- /packages/docs/src/scss/scene.scss: -------------------------------------------------------------------------------- 1 | /** Component: Scene */ 2 | 3 | #scene { 4 | position: fixed; 5 | top: 0px; 6 | width: 100%; 7 | height: 100%; 8 | z-index: 1000; 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/sitemap/URL-bundle-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/sitemap/URL-bundle-light.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/sitemap/custom-url-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/sitemap/custom-url-light.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/sitemap/multiple-sitemaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/sitemap/multiple-sitemaps.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/install/cli-select-content-types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/install/cli-select-content-types.png -------------------------------------------------------------------------------- /playground/src/api/home/routes/home.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * home router 3 | */ 4 | 5 | import { factories } from '@strapi/strapi'; 6 | 7 | export default factories.createCoreRouter('api::home.home'); 8 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/links/link-component-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/links/link-component-light.png -------------------------------------------------------------------------------- /playground/config/server.ts: -------------------------------------------------------------------------------- 1 | export default ({ env }) => ({ 2 | host: env('HOST', '0.0.0.0'), 3 | port: env.int('PORT', 1337), 4 | app: { 5 | keys: env.array('APP_KEYS'), 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /playground/src/api/home/services/home.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * home service 3 | */ 4 | 5 | import { factories } from '@strapi/strapi'; 6 | 7 | export default factories.createCoreService('api::home.home'); 8 | -------------------------------------------------------------------------------- /tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "**/*.cy.*", 5 | "./cypress/**/*" 6 | ], 7 | "include": [ 8 | "**/*.test.*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/links/link-custom-field-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/links/link-custom-field-light.png -------------------------------------------------------------------------------- /playground/src/api/home/controllers/home.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * home controller 3 | */ 4 | 5 | import { factories } from '@strapi/strapi' 6 | 7 | export default factories.createCoreController('api::home.home'); 8 | -------------------------------------------------------------------------------- /packages/core/server/util/isObject.ts: -------------------------------------------------------------------------------- 1 | export const isObject = ( 2 | value: any, 3 | ): value is Record => typeof value === 'object' && value !== null && !Array.isArray(value); 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/links/ckeditor-link-modal-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/links/ckeditor-link-modal-light.png -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/redirects/create-redirects-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/redirects/create-redirects-light.png -------------------------------------------------------------------------------- /packages/addons/sitemap/server/controllers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import core from './core'; 4 | import settings from './settings'; 5 | 6 | export default { 7 | core, 8 | settings, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/redirects/redirects-overview-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/redirects/redirects-overview-light.png -------------------------------------------------------------------------------- /playground/src/api/category/routes/category.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * category router 3 | */ 4 | 5 | import { factories } from '@strapi/strapi'; 6 | 7 | export default factories.createCoreRouter('api::category.category'); 8 | -------------------------------------------------------------------------------- /packages/core/admin/types/url-aliases.ts: -------------------------------------------------------------------------------- 1 | export interface UrlAliasEntity { 2 | id: number 3 | documentId: string 4 | url_path: string 5 | contenttype: string 6 | generated: boolean 7 | locale?: string 8 | } 9 | -------------------------------------------------------------------------------- /playground/src/api/category/services/category.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * category service 3 | */ 4 | 5 | import { factories } from '@strapi/strapi'; 6 | 7 | export default factories.createCoreService('api::category.category'); 8 | -------------------------------------------------------------------------------- /packages/core/admin/types/enabled-contenttypes.ts: -------------------------------------------------------------------------------- 1 | export interface EnabledContentType { 2 | name: string, 3 | uid: string, 4 | localized: boolean, 5 | } 6 | 7 | export type EnabledContentTypes = EnabledContentType[]; 8 | -------------------------------------------------------------------------------- /playground/.env: -------------------------------------------------------------------------------- 1 | 2 | HOST=0.0.0.0 3 | PORT=1337 4 | APP_KEYS="toBeModified1,toBeModified2" 5 | API_TOKEN_SALT=tobemodified 6 | TRANSFER_TOKEN_SALT=tobemodified 7 | ADMIN_JWT_SECRET=tobemodified 8 | JWT_SECRET=tobemodified 9 | -------------------------------------------------------------------------------- /playground/.env.example: -------------------------------------------------------------------------------- 1 | HOST=0.0.0.0 2 | PORT=1337 3 | APP_KEYS="toBeModified1,toBeModified2" 4 | API_TOKEN_SALT=tobemodified 5 | ADMIN_JWT_SECRET=tobemodified 6 | TRANSFER_TOKEN_SALT=tobemodified 7 | JWT_SECRET=tobemodified 8 | -------------------------------------------------------------------------------- /playground/src/api/category/controllers/category.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * category controller 3 | */ 4 | 5 | import { factories } from '@strapi/strapi' 6 | 7 | export default factories.createCoreController('api::category.category'); 8 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/addons/links/webtools-pro-link-addon-options-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluginpal/strapi-webtools/HEAD/packages/docs/static/img/assets/addons/links/webtools-pro-link-addon-options-light.png -------------------------------------------------------------------------------- /packages/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /playground/src/api/test/routes/test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * test router 5 | */ 6 | 7 | const { createCoreRouter } = require('@strapi/strapi').factories; 8 | 9 | export default createCoreRouter('api::test.test'); 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/after.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /playground/src/api/test/services/test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * test service 5 | */ 6 | 7 | const { createCoreService } = require('@strapi/strapi').factories; 8 | 9 | export default createCoreService('api::test.test'); 10 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | default: { 5 | cron: '0 0 0 * * *', 6 | limit: 45000, 7 | xsl: true, 8 | autoGenerate: false, 9 | }, 10 | validator() {}, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/cli/.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/playground 3 | **/public 4 | **/build 5 | **/dist 6 | **/bundle 7 | **/config 8 | **/scripts 9 | **/docs 10 | **/types/generated 11 | **/__tests__ 12 | strapi-admin.js 13 | strapi-server.js 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = LF 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /packages/addons/sitemap/server/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import adminRoutes from './admin'; 4 | import contentApi from './content-api'; 5 | 6 | export default { 7 | admin: adminRoutes, 8 | 'content-api': contentApi, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/core/.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/playground 3 | **/public 4 | **/build 5 | **/dist 6 | **/bundle 7 | **/config 8 | **/scripts 9 | **/docs 10 | **/types/generated 11 | **/__tests__ 12 | strapi-admin.js 13 | strapi-server.js 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/src/scss/images.scss: -------------------------------------------------------------------------------- 1 | /** General: Images */ 2 | 3 | /** Dark Mode images border */ 4 | .theme-doc-markdown.markdown [class*="themedImage--dark"] { 5 | border: 0.125rem solid var(--strapi-neutral-200); 6 | border-radius: 0.25rem; 7 | } 8 | -------------------------------------------------------------------------------- /playground/src/api/private-category/routes/private-category.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * private-category router 3 | */ 4 | 5 | import { factories } from '@strapi/strapi'; 6 | 7 | export default factories.createCoreRouter('api::private-category.private-category'); 8 | -------------------------------------------------------------------------------- /playground/src/api/test/controllers/test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * test controller 5 | */ 6 | 7 | const { createCoreController } = require('@strapi/strapi').factories; 8 | 9 | export default createCoreController('api::test.test'); 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | database: 5 | image: postgres 6 | environment: 7 | - POSTGRES_DB=strapi 8 | - POSTGRES_USER=strapi 9 | - POSTGRES_PASSWORD=strapi 10 | ports: 11 | - "5432:5432" -------------------------------------------------------------------------------- /packages/addons/sitemap/server/services/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import query from './query'; 4 | import core from './core'; 5 | import settings from './settings'; 6 | 7 | export default { 8 | query, 9 | core, 10 | settings, 11 | }; 12 | -------------------------------------------------------------------------------- /playground/src/api/private-category/services/private-category.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * private-category service 3 | */ 4 | 5 | import { factories } from '@strapi/strapi'; 6 | 7 | export default factories.createCoreService('api::private-category.private-category'); 8 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/provider.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /playground/src/api/private-category/controllers/private-category.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * private-category controller 3 | */ 4 | 5 | import { factories } from '@strapi/strapi' 6 | 7 | export default factories.createCoreController('api::private-category.private-category'); 8 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/state/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux-immutable'; 2 | import sitemapReducer from './Sitemap'; 3 | 4 | const rootReducer = combineReducers({ 5 | sitemap: sitemapReducer, 6 | }); 7 | 8 | export default rootReducer; 9 | -------------------------------------------------------------------------------- /packages/core/server/util/pluginId.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import pluginPkg from '../../package.json'; 4 | 5 | /** 6 | * A helper function to obtain the plugin id. 7 | * 8 | * @return {string} The plugin id. 9 | */ 10 | export const pluginId = pluginPkg.strapi.name; 11 | -------------------------------------------------------------------------------- /packages/docs/src/components/CustomDocCardsWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function CustomDocCardsWrapper({ children }) { 4 | return ( 5 |
6 | {children} 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/admin/index.cy.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | // 3 | 4 | describe('Webtools Core', () => { 5 | it('Load the homepage and check if somethings there', () => { 6 | cy.visit('/'); 7 | cy.contains('Welcome to Strapi'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /playground/src/components/core/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_core_headers", 3 | "info": { 4 | "displayName": "header" 5 | }, 6 | "options": {}, 7 | "attributes": { 8 | "title": { 9 | "type": "string" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/utils/pluginId.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import pluginPkg from '../../package.json'; 4 | 5 | /** 6 | * A helper function to obtain the plugin id. 7 | * 8 | * @return {string} The plugin id. 9 | */ 10 | export const pluginId = pluginPkg.strapi.name; 11 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/addons/sitemap/.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/playground 3 | **/public 4 | **/build 5 | **/dist 6 | **/bundle 7 | **/config 8 | **/scripts 9 | **/docs 10 | **/types 11 | **/xsl 12 | **/server/types/generated 13 | **/__tests__ 14 | strapi-admin.js 15 | strapi-server.js 16 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/components/PluginIcon/index.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * PluginIcon 4 | * 5 | */ 6 | 7 | import React from 'react'; 8 | 9 | import { BulletList } from '@strapi/icons'; 10 | 11 | const PluginIcon = () => ; 12 | 13 | export default PluginIcon; 14 | -------------------------------------------------------------------------------- /packages/core/admin/components/PluginIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | /** 4 | * 5 | * PluginIcon 6 | * 7 | */ 8 | 9 | import { Globe } from '@strapi/icons'; 10 | 11 | const PluginIcon = () => ; 12 | 13 | export { PluginIcon }; 14 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/helpers/pluginId.js: -------------------------------------------------------------------------------- 1 | import pluginPkg from '../../package.json'; 2 | 3 | /** 4 | * A helper function to obtain the plugin id. 5 | * 6 | * @return {string} The plugin id. 7 | */ 8 | const pluginId = pluginPkg.strapi.name; 9 | 10 | export default pluginId; 11 | -------------------------------------------------------------------------------- /packages/core/admin/helpers/pluginId.ts: -------------------------------------------------------------------------------- 1 | import pluginPkg from '../../package.json'; 2 | 3 | /** 4 | * A helper function to obtain the plugin id. 5 | * 6 | * @return {string} The plugin id. 7 | */ 8 | const pluginId: string = pluginPkg.strapi.name; 9 | 10 | export default pluginId; 11 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tsconfig.cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "**/*.test.*", 5 | "./*.config.*" 6 | ], 7 | "include": [ 8 | "**/*.cy.*", 9 | "./cypress/**/*" 10 | ], 11 | "compilerOptions": { 12 | "types": ["cypress", "node"] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/cli/src/utils/addons.ts: -------------------------------------------------------------------------------- 1 | import { Addon, AVAILABLE_ADDONS, PREMIUM_ADDONS } from '../constants/addons'; 2 | 3 | export function getAvailableAddons(): Addon[] { 4 | return AVAILABLE_ADDONS; 5 | } 6 | 7 | export function getPremiumAddons(): Addon[] { 8 | return PREMIUM_ADDONS; 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/code.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/cli/.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | node_modules 4 | stats.json 5 | package-lock.json 6 | 7 | # Cruft 8 | .DS_Store 9 | npm-debug.log 10 | .idea 11 | 12 | # Strapi 13 | .strapi-updater.json 14 | 15 | # Production build 16 | build 17 | dist 18 | bundle 19 | -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | node_modules 4 | stats.json 5 | package-lock.json 6 | 7 | # Cruft 8 | .DS_Store 9 | npm-debug.log 10 | .idea 11 | 12 | # Strapi 13 | .strapi-updater.json 14 | 15 | # Production build 16 | build 17 | dist 18 | bundle 19 | -------------------------------------------------------------------------------- /packages/core/admin/types/addons.ts: -------------------------------------------------------------------------------- 1 | export interface WebtoolsAddonInfo { 2 | info: { 3 | name: string 4 | icon: string 5 | displayName: string 6 | description: string 7 | required: false, 8 | kind: string 9 | webtoolsAddon: true, 10 | addonName: string 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/docs/src/scss/pagination-nav.scss: -------------------------------------------------------------------------------- 1 | /** Component: Pagination Nav */ 2 | 3 | .pagination-nav { 4 | --ifm-spacing-horizontal: var(--strapi-spacing-4); 5 | 6 | &__link { 7 | &:hover { 8 | --ifm-pagination-nav-color-hover: var(--custom-card-border-color); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /playground/config/middlewares.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'strapi::logger', 3 | 'strapi::errors', 4 | 'strapi::security', 5 | 'strapi::cors', 6 | 'strapi::poweredBy', 7 | 'strapi::query', 8 | 'strapi::body', 9 | 'strapi::session', 10 | 'strapi::favicon', 11 | 'strapi::public', 12 | ]; 13 | -------------------------------------------------------------------------------- /packages/docs/src/components/index.js: -------------------------------------------------------------------------------- 1 | export * from './Button/Button'; 2 | export * from './Card/Card'; 3 | export * from './Container/Container'; 4 | export * from './HomepageFeatures'; 5 | export * from './FeaturesList/FeaturesList'; 6 | export * from './Hero/Hero'; 7 | export * from './LinkWithArrow/LinkWithArrow'; 8 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/content_manager.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/addons/sitemap/.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | node_modules 4 | stats.json 5 | package-lock.json 6 | 7 | # Cruft 8 | .DS_Store 9 | npm-debug.log 10 | .idea 11 | 12 | # Strapi 13 | .strapi-updater.json 14 | 15 | # Production build 16 | build 17 | dist 18 | bundle 19 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/helpers/getSelectedContentType.js: -------------------------------------------------------------------------------- 1 | const getSelectedContentType = (contentTypes, uid) => { 2 | const selectedContentType = contentTypes.filter( 3 | (type) => type.uid === uid, 4 | )[0]; 5 | 6 | return selectedContentType; 7 | }; 8 | 9 | export default getSelectedContentType; 10 | -------------------------------------------------------------------------------- /packages/core/server/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | import generateUrlAlias from './generate-url-alias'; 2 | import preventDuplicateUrls from './prevent-duplicate-urls'; 3 | import deleteUrlAlias from './delete-url-alias'; 4 | 5 | export default { 6 | generateUrlAlias, 7 | preventDuplicateUrls, 8 | deleteUrlAlias, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/Clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/clear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src", 6 | "composite": true, 7 | "types": ["jest", "node"] 8 | }, 9 | "include": ["src/**/*", "src/**/*.test.ts"], 10 | "exclude": ["node_modules", "dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/server/content-types/index.ts: -------------------------------------------------------------------------------- 1 | import urlAliasSchema from './url-alias/schema.json'; 2 | import urlPatternSchema from './url-pattern/schema.json'; 3 | 4 | export default { 5 | 'url-alias': { 6 | schema: urlAliasSchema, 7 | }, 8 | 'url-pattern': { 9 | schema: urlPatternSchema, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/close-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/core/server/util/sanitizeOutput.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from '@strapi/strapi'; 2 | 3 | export const sanitizeOutput = ( 4 | data, 5 | contentType: Schema.ContentType, 6 | auth: unknown, 7 | ) => strapi.contentAPI.sanitize.output(data, contentType, { auth }); 8 | 9 | export default { 10 | sanitizeOutput, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/core/server/util/enabledContentTypes.ts: -------------------------------------------------------------------------------- 1 | import get from 'lodash/get'; 2 | import { Schema } from '@strapi/strapi'; 3 | 4 | 5 | export const isContentTypeEnabled = (contentType: Schema.ContentType) => { 6 | const { pluginOptions } = contentType; 7 | return get(pluginOptions, ['webtools', 'enabled'], false) as boolean; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/components/ModalForm/mapper.js: -------------------------------------------------------------------------------- 1 | export default { 2 | priority: { 3 | options: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], 4 | value: 0.5, 5 | }, 6 | changefreq: { 7 | options: ['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'], 8 | value: 'monthly', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/utils/enabledContentTypes.ts: -------------------------------------------------------------------------------- 1 | import get from 'lodash/get'; 2 | import { Schema } from '@strapi/strapi'; 3 | 4 | 5 | export const isContentTypeEnabled = (contentType: Schema.ContentType) => { 6 | const { pluginOptions } = contentType; 7 | return get(pluginOptions, ['webtools', 'enabled'], false) as boolean; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/list_view.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/screens/Overview/index.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Providers from '../../containers/Providers'; 3 | import Overview from '../../containers/Overview'; 4 | 5 | const SitemapOverview = () => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default SitemapOverview; 14 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/crop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/utils/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import { 5 | logMessage, 6 | } from '..'; 7 | 8 | describe('Generic utilities', () => { 9 | it('Log message formatting', () => { 10 | const message = logMessage('An error occurred'); 11 | 12 | expect(message).toEqual('[webtools-addon-sitemap]: An error occurred'); 13 | 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/docs/src/components/Container/Container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './container.module.scss'; 4 | 5 | export function Container({ className, ...rest }) { 6 | return ( 7 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/docs/src/scss/_fonts.scss: -------------------------------------------------------------------------------- 1 | /** Fonts */ 2 | 3 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;700&display=swap'); 4 | 5 | .font-poppins, 6 | .font-poppins button, 7 | .font-poppins input { 8 | --custom-font-family: "Poppins", sans-serif; 9 | --ifm-heading-font-family: "Poppins", sans-serif; 10 | 11 | font-family: var(--custom-font-family); 12 | } 13 | -------------------------------------------------------------------------------- /packages/docs/src/components/Request.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Request({ 4 | children, 5 | title = 'Example request', 6 | }) { 7 | return ( 8 |
9 |
{title}
10 |
{children}
11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /playground/src/admin/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { mergeConfig, type UserConfig } from 'vite'; 2 | 3 | export default (config: UserConfig) => { 4 | // Important: always return the modified config 5 | return mergeConfig(config, { 6 | resolve: { 7 | alias: { 8 | '@': '/src', 9 | }, 10 | }, 11 | optimizeDeps: { 12 | exclude: [], 13 | }, 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/admin/helpers/prefixPluginTranslations.js: -------------------------------------------------------------------------------- 1 | const prefixPluginTranslations = (trad, pluginId) => { 2 | if (!pluginId) { 3 | throw new TypeError('pluginId can not be empty'); 4 | } 5 | return Object.keys(trad).reduce((acc, current) => { 6 | acc[`${pluginId}.${current}`] = trad[current]; 7 | return acc; 8 | }, {}); 9 | }; 10 | 11 | export { prefixPluginTranslations }; 12 | -------------------------------------------------------------------------------- /packages/docs/src/components/Response.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Response({ 4 | children, 5 | title = 'Example response', 6 | }) { 7 | return ( 8 |
9 |
{title}
10 |
{children}
11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/docs/src/scss/table.scss: -------------------------------------------------------------------------------- 1 | /** Component: Table */ 2 | table { 3 | min-width: 100%; 4 | overflow: auto; 5 | 6 | thead { 7 | --ifm-table-background: transparent; 8 | --ifm-table-stripe-background: transparent; 9 | 10 | tr { 11 | border-bottom-width: 1px; 12 | 13 | th { 14 | --ifm-table-head-background: transparent; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /playground/tests/healthcheck.test.js: -------------------------------------------------------------------------------- 1 | const { setupStrapi, stopStrapi } = require('./helpers'); 2 | 3 | jest.setTimeout(50000); 4 | 5 | beforeAll(async () => { 6 | await setupStrapi(); 7 | }); 8 | 9 | afterAll(async () => { 10 | await stopStrapi(); 11 | }); 12 | 13 | describe('Strapi is defined', () => { 14 | it('just works', () => { 15 | expect(strapi).toBeDefined(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/docs/blog/2019-05-28-first-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: first-blog-post 3 | title: First Blog Post 4 | authors: [slorber, yangshun] 5 | tags: [hola, docusaurus] 6 | --- 7 | 8 | Lorem ipsum dolor sit amet... 9 | 10 | 11 | 12 | ...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 13 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/helpers/prefixPluginTranslations.js: -------------------------------------------------------------------------------- 1 | const prefixPluginTranslations = (trad, pluginId) => { 2 | if (!pluginId) { 3 | throw new TypeError('pluginId can not be empty'); 4 | } 5 | return Object.keys(trad).reduce((acc, current) => { 6 | acc[`${pluginId}.${current}`] = trad[current]; 7 | return acc; 8 | }, {}); 9 | }; 10 | 11 | export { prefixPluginTranslations }; 12 | -------------------------------------------------------------------------------- /packages/docs/src/components/ApiCall.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import clsx from 'clsx' 3 | 4 | export default function ApiCall({ 5 | children, 6 | noSideBySide = false, 7 | }) { 8 | return ( 9 |
15 | {children} 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/migrations/migrate-config-object-to-array.js: -------------------------------------------------------------------------------- 1 | import packageJson from '../../package.json'; 2 | 3 | export const migrateToNewConfigObject = (oldConfig) => { 4 | return { 5 | version: packageJson.version, 6 | sitemaps: { 7 | default: oldConfig, 8 | }, 9 | }; 10 | }; 11 | 12 | export const isOldConfigFormat = (config) => { 13 | return config.version === undefined; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/docs/docs/cli/enable.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Enable' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /cli/enable 5 | --- 6 | 7 | # Enable 8 | 9 | ## Overview 10 | 11 | This command let's you interactively enable Webtools for your content types. It's essentially a programmatic interface for the docs as written on [the Usage docs](/usage). 12 | 13 | ## Usage 14 | 15 | ``` 16 | npx webtools-cli enable 17 | ``` 18 | -------------------------------------------------------------------------------- /playground/config/plugins.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webtools: { 3 | enabled: true, 4 | config: { 5 | website_url: 'https://www.pluginpal.io', 6 | }, 7 | }, 8 | 9 | 'webtools-addon-sitemap': { 10 | enabled: true, 11 | }, 12 | 13 | // 'webtools-addon-redirects': { 14 | // enabled: true, 15 | // }, 16 | 17 | // 'webtools-addon-menus': { 18 | // enabled: true, 19 | // }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/core/admin/components/Center/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren, FC } from 'react'; 2 | 3 | type Props = PropsWithChildren<{}>; 4 | 5 | const Center: FC = ({ children }) => ( 6 |
13 | {children} 14 |
15 | ); 16 | 17 | export default Center; 18 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/CheckCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/docs/api/graphql.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'GraphQL API' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /api/graphql 5 | --- 6 | 7 | # GraphQL API 8 | 9 | ## Overview 10 | 11 | As of writing this page there is no GraphQL API exposed by the Webtools core plugin. You might be able to still get most of its functionality by querying the native Strapi GraphQL API, but this is not officially supported at this time. 12 | 13 | -------------------------------------------------------------------------------- /packages/core/server/services/index.ts: -------------------------------------------------------------------------------- 1 | import urlAliasService from './url-alias'; 2 | import urlPatternService from './url-pattern'; 3 | import bulkGenerateService from './bulk-generate'; 4 | import getMainFieldService from './get-main-field'; 5 | 6 | export default { 7 | 'url-alias': urlAliasService, 8 | 'url-pattern': urlPatternService, 9 | 'bulk-generate': bulkGenerateService, 10 | 'get-main-field': getMainFieldService, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/core/server/types/koa.ts: -------------------------------------------------------------------------------- 1 | import { Context, Request } from 'koa'; 2 | 3 | interface KoaRequest extends Request { 4 | body?: RequestBody; 5 | } 6 | 7 | export interface KoaContext extends Context { 8 | request: KoaRequest; 9 | body: ResponseBody; 10 | } 11 | 12 | export interface KoaResponseContext extends KoaContext {} 13 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalDependencies": ["**/.env.*local"], 4 | "globalEnv": ["LOCALAPPDATA"], 5 | "tasks": { 6 | "build": { 7 | "cache": false, 8 | "dependsOn": ["^build"] 9 | }, 10 | "eslint": { 11 | "outputs": [] 12 | }, 13 | "eslint:fix": { 14 | "outputs": [] 15 | }, 16 | "watch:link": { 17 | "cache": false 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/docs/blog/tags.yml: -------------------------------------------------------------------------------- 1 | facebook: 2 | label: Facebook 3 | permalink: /facebook 4 | description: Facebook tag description 5 | 6 | hello: 7 | label: Hello 8 | permalink: /hello 9 | description: Hello tag description 10 | 11 | docusaurus: 12 | label: Docusaurus 13 | permalink: /docusaurus 14 | description: Docusaurus tag description 15 | 16 | hola: 17 | label: Hola 18 | permalink: /hola 19 | description: Hola tag description 20 | -------------------------------------------------------------------------------- /packages/docs/src/scss/columns.scss: -------------------------------------------------------------------------------- 1 | /** Components: Columns */ 2 | 3 | .column { 4 | &__title { 5 | font-weight: 700; 6 | margin-bottom: var(--strapi-spacing-1); 7 | } 8 | } 9 | 10 | /** Responsive */ 11 | @include medium-up { 12 | .columns { 13 | display: flex; 14 | justify-content: space-between; 15 | gap: var(--strapi-spacing-4); 16 | 17 | > .column { 18 | flex: 1; 19 | max-width: 50%; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /playground/config/database.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | export default ({ env }) => ({ 4 | connection: { 5 | client: 'sqlite', 6 | connection: { 7 | filename: path.join( 8 | __dirname, 9 | '..', 10 | // We need to go back once more to get out of the dist folder 11 | '..', 12 | env('DATABASE_FILENAME', '.tmp/data.db'), 13 | ), 14 | }, 15 | useNullAsDefault: true, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /packages/docs/src/scss/medium-zoom.scss: -------------------------------------------------------------------------------- 1 | /** Component: Medium Zoom */ 2 | 3 | .medium-zoom { 4 | &-overlay { 5 | background: var(--strapi-neutral-150) !important; 6 | } 7 | } 8 | 9 | /** Responsive */ 10 | @include medium-up { 11 | .medium-zoom { 12 | &-image { 13 | &--opened { 14 | margin-top: var(--ifm-navbar-height) !important; 15 | margin-bottom: var(--ifm-navbar-height) !important; 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/index.js: -------------------------------------------------------------------------------- 1 | import bootstrap from './bootstrap'; 2 | import register from './register'; 3 | import services from './services'; 4 | import routes from './routes'; 5 | import config from './config'; 6 | import controllers from './controllers'; 7 | import contentTypes from './content-types'; 8 | 9 | export default { 10 | bootstrap, 11 | register, 12 | routes, 13 | config, 14 | controllers, 15 | services, 16 | contentTypes, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/core/server/index.ts: -------------------------------------------------------------------------------- 1 | 2 | import register from './register'; 3 | import bootstrap from './bootstrap'; 4 | import routes from './routes'; 5 | import controllers from './controllers'; 6 | import services from './services'; 7 | import config from './config'; 8 | import contentTypes from './content-types'; 9 | 10 | export default { 11 | register, 12 | bootstrap, 13 | config, 14 | contentTypes, 15 | routes, 16 | controllers, 17 | services, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/screens/Settings/index.jsx: -------------------------------------------------------------------------------- 1 | import { useParams } from 'react-router-dom'; 2 | import * as React from 'react'; 3 | import Providers from '../../containers/Providers'; 4 | import Settings from '../../containers/Settings'; 5 | 6 | const SitemapSettings = () => { 7 | const { id } = useParams(); 8 | 9 | return ( 10 | 11 | 12 | 13 | ); 14 | }; 15 | 16 | export default SitemapSettings; 17 | -------------------------------------------------------------------------------- /packages/core/server/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import urlAliasController from './url-alias'; 2 | import urlPatternController from './url-pattern'; 3 | import infoController from './info'; 4 | import coreController from './core'; 5 | import searchController from './search'; 6 | 7 | export default { 8 | 'url-alias': urlAliasController, 9 | 'url-pattern': urlPatternController, 10 | info: infoController, 11 | core: coreController, 12 | search: searchController, 13 | }; 14 | -------------------------------------------------------------------------------- /cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('cypress'); 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:1337', 6 | specPattern: '**/*.cy.{js,ts,jsx,tsx}', 7 | setupNodeEvents(on, config) { 8 | // implement node event listeners here. 9 | require('cypress-terminal-report/src/installLogsPrinter')(on); 10 | }, 11 | video: true, 12 | defaultCommandTimeout: 10000, 13 | requestTimeout: 10000, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/settings/include-homepage.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Include homepage' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/settings/include-homepage 5 | --- 6 | 7 | # Include homepage 8 | 9 | This setting will add a default `/` entry to the sitemap XML when none is present. The `/` entry corresponds to the homepage of your website. 10 | 11 | ###### Key: `includeHomepage` 12 | 13 | > `required:` NO | `type:` bool | `default:` true 14 | -------------------------------------------------------------------------------- /playground/config/admin.ts: -------------------------------------------------------------------------------- 1 | export default ({ env }) => ({ 2 | auth: { 3 | secret: env('ADMIN_JWT_SECRET'), 4 | }, 5 | apiToken: { 6 | salt: env('API_TOKEN_SALT'), 7 | }, 8 | transfer: { 9 | token: { 10 | salt: env('TRANSFER_TOKEN_SALT'), 11 | }, 12 | }, 13 | flags: { 14 | nps: env.bool('FLAG_NPS', true), 15 | promoteEE: env.bool('FLAG_PROMOTE_EE', true), 16 | }, 17 | watchIgnoreFiles: [ 18 | '!**/.yalc/**/server/**', 19 | ], 20 | }); 21 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/permissions.js: -------------------------------------------------------------------------------- 1 | const pluginPermissions = { 2 | // This permission regards the main component (App) and is used to tell 3 | // If the plugin link should be displayed in the menu 4 | // And also if the plugin is accessible. This use case is found when a user types the url of the 5 | // plugin directly in the browser 6 | settings: [{ action: 'plugin::webtools-addon-sitemap.settings.read', subject: null }], 7 | }; 8 | 9 | export default pluginPermissions; 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/documentation-plugin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/core/admin/types/url-patterns.ts: -------------------------------------------------------------------------------- 1 | export interface PatternEntity { 2 | id: number 3 | documentId: string; 4 | pattern: string 5 | contenttype: string 6 | languages: any[] 7 | createdAt: string 8 | updatedAt: string 9 | } 10 | 11 | export interface PatternFormValues { 12 | pattern: string, 13 | contenttype: string, 14 | languages: any[], 15 | localized: boolean, 16 | } 17 | 18 | export interface ValidatePatternResponse { 19 | message: string, 20 | valid: boolean, 21 | } 22 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/settings/language-filter.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Language filter' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/settings/language-filter 5 | --- 6 | 7 | # Language filter 8 | 9 | With this setting you can setup language specific sitemaps. When you select a language here then only pages from that languages will be included in the sitemap. 10 | 11 | ###### Key: `languageFilter` 12 | 13 | > `required:` NO | `type:` string | `default:` 'off' 14 | -------------------------------------------------------------------------------- /packages/docs/src/scss/grid.scss: -------------------------------------------------------------------------------- 1 | /** Component: Grid */ 2 | 3 | .row { 4 | --custom-grid-spacing: var(--strapi-spacing-2); 5 | --ifm-spacing-horizontal: var(--custom-grid-spacing); 6 | 7 | .col { 8 | &.margin-bottom--lg { 9 | margin-bottom: calc(var(--custom-grid-spacing) * 2) !important; 10 | } 11 | } 12 | } 13 | 14 | /** Responsive */ 15 | @include medium-up { 16 | .row { 17 | &--huge { 18 | --custom-grid-spacing: var(--strapi-spacing-4); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /playground/src/api/home/content-types/home/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "singleType", 3 | "collectionName": "homes", 4 | "info": { 5 | "singularName": "home", 6 | "pluralName": "homes", 7 | "displayName": "Home", 8 | "description": "" 9 | }, 10 | "options": { 11 | "draftAndPublish": false 12 | }, 13 | "pluginOptions": { 14 | "webtools": { 15 | "enabled": true 16 | } 17 | }, 18 | "attributes": { 19 | "title": { 20 | "type": "string" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/admin/helpers/useActiveElement.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => { 4 | const [active, setActive] = React.useState(document.activeElement); 5 | 6 | const handleFocusIn = () => { 7 | setActive(document.activeElement); 8 | }; 9 | 10 | React.useEffect(() => { 11 | document.addEventListener('focusin', handleFocusIn); 12 | return () => { 13 | document.removeEventListener('focusin', handleFocusIn); 14 | }; 15 | }, []); 16 | 17 | return active; 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /packages/docs/src/components/SubtleCallout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function SubtleCallout({ 4 | children, 5 | title, 6 | emoji = '🤓', 7 | }) { 8 | return ( 9 |
10 |
11 | { emoji } { title } 12 |
13 | { children } 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/check_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /playground/types/generated/components.d.ts: -------------------------------------------------------------------------------- 1 | import type { Schema, Struct } from '@strapi/strapi'; 2 | 3 | export interface CoreHeader extends Struct.ComponentSchema { 4 | collectionName: 'components_core_headers'; 5 | info: { 6 | displayName: 'header'; 7 | }; 8 | attributes: { 9 | title: Schema.Attribute.String; 10 | }; 11 | } 12 | 13 | declare module '@strapi/strapi' { 14 | export module Public { 15 | export interface ComponentSchemas { 16 | 'core.header': CoreHeader; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/helpers/useActiveElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => { 4 | const [active, setActive] = React.useState(document.activeElement); 5 | 6 | const handleFocusIn = (e) => { 7 | setActive(document.activeElement); 8 | }; 9 | 10 | React.useEffect(() => { 11 | document.addEventListener('focusin', handleFocusIn); 12 | return () => { 13 | document.removeEventListener('focusin', handleFocusIn); 14 | }; 15 | }, []); 16 | 17 | return active; 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/move.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/email_template.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | testMatch: ['**/__tests__/?(*.)+(spec|test).(ts|js)'], 5 | // transform: {}, 6 | // globalSetup: './playground/__tests__/setup-strapi.ts', 7 | // setupFilesAfterEnv: ['./playground/__tests__/setup-strapi.ts'], 8 | // globalTeardown: './playground/__tests__/teardown-strapi.ts', 9 | coverageDirectory: './coverage/', 10 | collectCoverage: true, 11 | transform: { 12 | '^.+\\.(ts|tsx|js|jsx)$': 'ts-jest', 13 | }, 14 | testTimeout: 30_000, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/admin/screens/Patterns/EditPage/utils/schema.ts: -------------------------------------------------------------------------------- 1 | import * as yup from 'yup'; 2 | import { translatedErrors } from '@strapi/strapi/admin'; 3 | 4 | const schema = yup.object().shape({ 5 | pattern: yup.string().required(translatedErrors.required), 6 | contenttype: yup.string().required(translatedErrors.required), 7 | languages: yup.array().when('localized', { 8 | is: true, 9 | then: yup.array().min(1, 'Select at least one language'), 10 | otherwise: yup.array().notRequired(), 11 | }), 12 | }); 13 | 14 | export default schema; 15 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/settings/exclude-drafts.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Exclude drafts' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/settings/exclude-drafts 5 | --- 6 | 7 | # Exclude drafts 8 | 9 | When using the draft/publish functionality in Strapi this setting will make sure that all draft pages are excluded from the sitemap. If you want to have the draft pages in the sitemap anyways you can disable this setting. 10 | 11 | ###### Key: `excludeDrafts` 12 | 13 | > `required:` NO | `type:` bool | `default:` true 14 | -------------------------------------------------------------------------------- /packages/core/admin/screens/Patterns/CreatePage/utils/schema.ts: -------------------------------------------------------------------------------- 1 | import * as yup from 'yup'; 2 | import { translatedErrors } from '@strapi/strapi/admin'; 3 | 4 | const schema = () => yup.object().shape({ 5 | pattern: yup.string().required(translatedErrors.required), 6 | contenttype: yup.string().required(translatedErrors.required), 7 | languages: yup.array().when('localized', { 8 | is: true, 9 | then: yup.array().min(1, 'Select at least one language'), 10 | otherwise: yup.array().notRequired(), 11 | }), 12 | }); 13 | 14 | export default schema; 15 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/configuration/cron.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Cron' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/configuration/cron 5 | --- 6 | 7 | # CRON 8 | 9 | To make sure the sitemap stays up-to-date this plugin will automatically schedule a cron job that generates the sitemap for you. That cron job is configured to run once a day at 00:00. 10 | 11 | If you want to change the cron interval you can alter the `cron` setting. 12 | 13 | ###### Key: `cron ` 14 | 15 | > `required:` NO | `type:` bool | `default:` 0 0 0 * * * 16 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /playground/config/env/test/database.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | export default ({ env }) => ({ 4 | connection: { 5 | client: 'sqlite', 6 | connection: { 7 | filename: path.join( 8 | __dirname, 9 | // Get out of config/env/test 10 | '..', 11 | '..', 12 | '..', 13 | // We need to go back once more to get out of the dist folder 14 | '..', 15 | env('DATABASE_TEST_FILENAME', '.tmp/test.db'), 16 | ), 17 | }, 18 | useNullAsDefault: true, 19 | debug: false, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /packages/docs/src/components/Container/container.module.scss: -------------------------------------------------------------------------------- 1 | /** Component: Container */ 2 | 3 | :root { 4 | --strapi-container-px: var(--ifm-spacing-horizontal); 5 | --strapi-container-mw: calc(863px + calc(var(--strapi-container-px) * 2)); 6 | } 7 | 8 | .container { 9 | display: flex; 10 | flex-direction: column; 11 | align-items: stretch; 12 | margin-right: auto; 13 | margin-left: auto; 14 | padding-right: var(--strapi-container-px); 15 | padding-left: var(--strapi-container-px); 16 | max-width: var(--strapi-container-mw); 17 | width: 100%; 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/scss/footer.scss: -------------------------------------------------------------------------------- 1 | /** Component: Footer */ 2 | 3 | .footer { 4 | a { 5 | @include transition; 6 | font-weight: 400; 7 | 8 | &:hover { 9 | font-weight: 700; 10 | } 11 | } 12 | 13 | &.footer--dark { 14 | --ifm-footer-background-color: var(--strapi-neutral-700); 15 | --ifm-footer-link-hover-color: var(--strapi-neutral-0); 16 | } 17 | } 18 | 19 | /** Dark mode */ 20 | @include dark { 21 | .footer { 22 | &.footer--dark { 23 | --ifm-footer-background-color: var(--strapi-neutral-150); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ONHOLDCarretDown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ONHOLDCarretUp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/add_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/src/scss/markdown.scss: -------------------------------------------------------------------------------- 1 | /** Component: Markdown */ 2 | 3 | $selector-markdown: ".theme-doc-markdown.markdown"; 4 | 5 | #{$selector-markdown} { 6 | --custom-markdown-pt: var(--strapi-spacing-0); 7 | --custom-markdown-pb: var(--strapi-spacing-2); 8 | 9 | --markdown-link-color: var(--strapi-primary-600); 10 | 11 | font-weight: 400; 12 | padding: var(--custom-markdown-pt) 0 var(--custom-markdown-pb); 13 | } 14 | 15 | /** Dark mode */ 16 | @include dark { 17 | #{$selector-markdown} { 18 | --markdown-link-color: var(--strapi-primary-600); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/releases.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/core/server/util/getPluginService.ts: -------------------------------------------------------------------------------- 1 | import { pluginId } from './pluginId'; 2 | import type config from '..'; 3 | 4 | type Config = typeof config; 5 | type Services = Config['services']; 6 | /** 7 | * A helper function to obtain a plugin service. 8 | * @param {string} name The name of the service. 9 | * 10 | * @return {any} service. 11 | */ 12 | export const getPluginService = (name: ServiceName) => { 13 | const service = strapi.service(`plugin::${pluginId}.${name}`); 14 | return service as ReturnType; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/carret.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/getting-started/robots-txt.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Robots.txt' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/robots-txt 5 | --- 6 | 7 | # 🤖 Robots.txt 8 | 9 | To make sure search engines are able to find the sitemap XML create a `robots.txt` file in the front-end of your website and add the following line: 10 | 11 | ``` 12 | Sitemap: https://your-strapi-domain.com/api/sitemap/index.xml 13 | ``` 14 | 15 | Read more about the `robots.txt` file [here](https://developers.google.com/search/docs/advanced/robots/create-robots-txt). 16 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/grid_view.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/utils/getPluginService.ts: -------------------------------------------------------------------------------- 1 | import type config from '..'; 2 | import { pluginId } from './pluginId'; 3 | 4 | type Config = typeof config; 5 | type Services = Config['services']; 6 | /** 7 | * A helper function to obtain a plugin service. 8 | * @param {string} name The name of the service. 9 | * 10 | * @return {any} service. 11 | */ 12 | export const getPluginService = (name: ServiceName) => { 13 | const service = strapi.service(`plugin::${pluginId}.${name}`); 14 | return service as ReturnType; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/verified-marketplace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "pluginpal/strapi-webtools" } 6 | ], 7 | "commit": false, 8 | "privatePackages": { 9 | "version": true, 10 | "tag": true 11 | }, 12 | "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { 13 | "onlyUpdatePeerDependentsWhenOutOfRange": true 14 | }, 15 | "fixed": [], 16 | "linked": [], 17 | "access": "public", 18 | "baseBranch": "master", 19 | "updateInternalDependencies": "patch", 20 | "ignore": [] 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/server/util/getAddons.ts: -------------------------------------------------------------------------------- 1 | const getAddons = () => { 2 | const enabledPlugins = strapi.config.get('enabledPlugins'); 3 | 4 | const addons: { 5 | [key: string]: unknown 6 | } = {}; 7 | 8 | Object.keys(enabledPlugins).forEach((plugin) => { 9 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 10 | const webtoolsAddon = enabledPlugins[plugin]?.info?.webtoolsAddon as boolean; 11 | 12 | if (webtoolsAddon === true) { 13 | addons[plugin] = enabledPlugins[plugin]; 14 | } 15 | }); 16 | 17 | return addons; 18 | }; 19 | 20 | export default getAddons; 21 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/down2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/application.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/up2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/notifications.svg: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/components/Loader/index.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Loader as LoaderComponent } from '@strapi/design-system'; 3 | 4 | const Loader = () => { 5 | const style = { 6 | display: 'flex', 7 | justifyContent: 'center', 8 | height: '100%', 9 | width: '100%', 10 | position: 'absolute', 11 | backgroundColor: 'rgba(255,255,255, 0.6)', 12 | zIndex: 1, 13 | alignItems: 'center', 14 | }; 15 | 16 | return ( 17 |
18 | Loading content... 19 |
20 | ); 21 | }; 22 | 23 | export default Loader; 24 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/redirects/configuration/default-status-code.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Default status code' 3 | displayed_sidebar: webtoolsRedirectsSidebar 4 | slug: /addons/redirects/configuration/default-status-code 5 | --- 6 | 7 | # Default status code 8 | 9 | This is the default status code that is used when new redirects are created. It does not matter if they are created through the admin interface, the document service or automatically generated, it will always use this default status code except when you overwrite it. 10 | 11 | ###### Key: `default_status_code` 12 | 13 | > `required:` NO | `type:` bool | `default:` 307 14 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/add_circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/webhooks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/core/admin/components/Loader/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Loader as LoaderComponent } from '@strapi/design-system'; 3 | 4 | const Loader = () => { 5 | const style: React.CSSProperties = { 6 | display: 'flex', 7 | justifyContent: 'center', 8 | position: 'absolute', 9 | height: '100%', 10 | width: '100%', 11 | backgroundColor: 'rgba(255,255,255, 0.6)', 12 | zIndex: 1, 13 | alignItems: 'center', 14 | }; 15 | 16 | return ( 17 |
18 | Loading content... 19 |
20 | ); 21 | }; 22 | 23 | export default Loader; 24 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_relation_oneway.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/blog/2021-08-01-mdx-blog-post.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: mdx-blog-post 3 | title: MDX Blog Post 4 | authors: [slorber] 5 | tags: [docusaurus] 6 | --- 7 | 8 | Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). 9 | 10 | :::tip 11 | 12 | Use the power of React to create interactive blog posts. 13 | 14 | ::: 15 | 16 | {/* truncate */} 17 | 18 | For example, use JSX to create an interactive button: 19 | 20 | ```js 21 | 22 | ``` 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/links/api/links-format.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Links format' 3 | displayed_sidebar: webtoolsLinksSidebar 4 | slug: /addons/links/api/links-format 5 | --- 6 | 7 | # Rewrite middleware 8 | 9 | This plugin stores the internal links in a specific format. That format looks something like this: 10 | 11 | ``` 12 | wt-link://api::page.page/sp8bzbkn21pjy3m9wsf97dio 13 | ``` 14 | 15 | This makes it possible to fetch the page, and ultimately the URL alias of the document that you have linked to. 16 | 17 | The plugin also provides a middleware which will rewrite these link formats to the actual link when you're using the Strapi API. 18 | 19 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/redirects/configuration/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Introduction' 3 | displayed_sidebar: webtoolsRedirectsSidebar 4 | slug: /addons/redirects/configuration 5 | --- 6 | 7 | # 🔧 Configuration 8 | The configuration of the plugin can be overridden in the `config/plugins.js` file. 9 | In the example below you can see how, and also what the default settings are. 10 | 11 | ```md title="config/plugins.js" 12 | module.exports = ({ env }) => ({ 13 | 'webtools-addon-redirects': { 14 | enabled: true, 15 | config: { 16 | default_status_code: 307, 17 | auto_generate: true, 18 | }, 19 | }, 20 | }); 21 | ``` 22 | -------------------------------------------------------------------------------- /packages/docs/src/scss/search.scss: -------------------------------------------------------------------------------- 1 | /** Component: Search */ 2 | 3 | :root body { 4 | --docsearch-hit-height: 56px; 5 | --docsearch-searchbox-height: 40px; 6 | --docsearch-spacing: var(--strapi-spacing-4); 7 | } 8 | 9 | body .DocSearch { 10 | --custom-search-hit-pb: var(--strapi-spacing-2); 11 | 12 | &-SearchBar { 13 | padding-bottom: var(--strapi-spacing-1); 14 | } 15 | 16 | &-Input { 17 | --docsearch-text-color: var(--strapi-neutral-800); 18 | } 19 | 20 | &-Input, 21 | &-Cancel { 22 | font-size: var(--strapi-font-size-md); 23 | } 24 | 25 | &-Hit { 26 | padding-bottom: var(--custom-search-hit-pb); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/documentation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/docs/cli/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Install' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /cli/install 5 | --- 6 | 7 | # Install 8 | 9 | ## Overview 10 | 11 | The install command is used when you're first installing Webtools in your Strapi project. It helps you to setup your license, enable Webtools for your content types, and select the addons you want to install. 12 | 13 | ## Usage 14 | 15 | ``` 16 | npx webtools-cli install 17 | ``` 18 | 19 | Apart from installing the packages, the functions of this command can also be ran separately. 20 | 21 | 1. [The `enable` command](/cli/enable) 22 | 2. [The `setup-license` command](/cli/setup-license) 23 | -------------------------------------------------------------------------------- /packages/core/admin/screens/List/components/PaginationFooter/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '@strapi/design-system'; 3 | import { Pagination as StrapiPagination } from '@strapi/strapi/admin'; 4 | import type { Pagination } from '../..'; 5 | 6 | type Props = { 7 | pagination: Pagination; 8 | }; 9 | 10 | const PaginationFooter = ({ pagination }: Props) => { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | }; 20 | 21 | export default PaginationFooter; 22 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/configuration/limit.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Limit' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/configuration/limit 5 | --- 6 | 7 | # Limit 8 | 9 | When creating large sitemaps (50.000+ URLs) you might want to split the sitemap in to chunks that you bring together in a sitemap index. 10 | 11 | The limit is there to specify the maximum amount of URL a single sitemap may hold. If you try to add more URLs to a single sitemap.xml it will automatically be split up in to chunks which are brought together in a single sitemap index. 12 | 13 | ###### Key: `limit ` 14 | 15 | > `required:` NO | `type:` int | `default:` 45000 16 | -------------------------------------------------------------------------------- /playground/src/admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "useDefineForClassFields": true, 7 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 8 | "allowJs": false, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "resolveJsonModule": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx" 17 | }, 18 | "include": ["../plugins/**/admin/src/**/*", "./"], 19 | "exclude": ["node_modules/", "build/", "dist/", "**/*.test.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/configuration/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Introduction' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/configuration 5 | --- 6 | 7 | # 🔧 Configuration 8 | The configuration of the plugin can be overridden in the `config/plugins.js` file. 9 | In the example below you can see how, and also what the default settings are. 10 | 11 | ```md title="config/plugins.js" 12 | module.exports = ({ env }) => ({ 13 | 'webtools-addon-sitemap': { 14 | enabled: true, 15 | config: { 16 | cron: '0 0 0 * * *', 17 | limit: 45000, 18 | xsl: true, 19 | autoGenerate: false, 20 | }, 21 | }, 22 | }); 23 | ``` 24 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/containers/Providers/index.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * This component is the skeleton around the actual pages, and should only 4 | * contain code that should be seen on all pages. (e.g. navigation bar) 5 | * 6 | */ 7 | 8 | import React from 'react'; 9 | import { Provider } from 'react-redux'; 10 | // import { CheckPagePermissions } from '@strapi/helper-plugin'; 11 | 12 | import { store } from '../../helpers/configureStore'; 13 | // import pluginPermissions from '../../permissions'; 14 | 15 | const Providers = ({ children }) => { 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | export default Providers; 24 | -------------------------------------------------------------------------------- /packages/core/server/config.ts: -------------------------------------------------------------------------------- 1 | import deburr from 'lodash/deburr'; 2 | import toLower from 'lodash/toLower'; 3 | import kebabCase from 'lodash/kebabCase'; 4 | 5 | export interface Config { 6 | website_url: string; 7 | default_pattern: string, 8 | unique_per_locale: boolean, 9 | slugify: (fieldValue: string) => string, 10 | } 11 | 12 | const config: { 13 | default: Config, 14 | validator: () => void 15 | } = { 16 | default: { 17 | website_url: null, 18 | default_pattern: '/[pluralName]/[documentId]', 19 | slugify: (fieldValue) => kebabCase(deburr(toLower(fieldValue))), 20 | unique_per_locale: false, 21 | }, 22 | validator() {}, 23 | }; 24 | 25 | export default config; 26 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/settings/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Introduction' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/settings 5 | --- 6 | 7 | import ThemedImage from '@theme/ThemedImage'; 8 | import useBaseUrl from '@docusaurus/useBaseUrl'; 9 | 10 | # Settings 11 | Settings can be changed in the admin section of the plugin. In the last tab (Settings) you will find the settings as described below. 12 | 13 | 20 | -------------------------------------------------------------------------------- /packages/core/admin/components/HiddenLocalizedField/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { FormikErrors } from 'formik'; 4 | 5 | type Props = { 6 | localized: boolean; 7 | setFieldValue: 8 | (field: string, value: any, shouldValidate?: boolean) => 9 | Promise> 10 | }; 11 | 12 | const HiddenLocalizedField = (props: Props) => { 13 | const { localized, setFieldValue } = props; 14 | 15 | React.useEffect(() => { 16 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 17 | setFieldValue('localized', localized); 18 | }, [localized, setFieldValue]); 19 | 20 | return null; 21 | }; 22 | 23 | export default HiddenLocalizedField; 24 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/settings/hostname.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Hostname' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/settings/hostname 5 | --- 6 | 7 | # Hostname (required) 8 | 9 | The hostname is the URL of your website. It will be used as the base URL of all URLs added to the sitemap XML. It is required to generate the XML file. 10 | 11 | :::note 12 | This setting belongs to the Sitemap addon and is not the same as the core Webtools "Website URL" configuration used for permalinks. For the core setting, see [Website URL](/webtools/configuration/website-url). 13 | ::: 14 | 15 | ###### Key: `hostname` 16 | 17 | > `required:` YES | `type:` string | `default:` '' 18 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/core/admin/types/content-api.ts: -------------------------------------------------------------------------------- 1 | interface DocumentData { 2 | documentId: string; 3 | createdAt: string; 4 | updatedAt: string; 5 | [key: string]: any; 6 | } 7 | 8 | interface Pagination { 9 | page: number; 10 | pageSize: number; 11 | pageCount: number; 12 | total: number; 13 | } 14 | 15 | interface Meta { 16 | pagination?: Pagination; 17 | } 18 | 19 | export interface GenericResponse { 20 | data: T; 21 | meta: Meta; 22 | } 23 | 24 | export interface GenericContentManagerResponse { 25 | results: T; 26 | pagination: Pagination; 27 | } 28 | 29 | export type GenericDocumentResponse = GenericResponse; 30 | export type GenericMultiDocumentResponse = GenericResponse; 31 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/getting-started/multilingual.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Multilingual' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/multilingual 5 | --- 6 | 7 | # 🌍 Multilingual 8 | 9 | When adding a URL bundle of a type which has localizations enabled you will be presented with a language dropdown in the settings form. You can now set a different URL pattern for each language. 10 | 11 | For each localization of a page the `` in the sitemap XML will get an extra attribute: 12 | 13 | - `` 14 | 15 | This implementation is based on [Google's guidelines](https://developers.google.com/search/docs/advanced/crawling/localized-versions) on localized sitemaps. 16 | -------------------------------------------------------------------------------- /playground/src/admin/app.example.tsx: -------------------------------------------------------------------------------- 1 | import type { StrapiApp } from '@strapi/strapi/admin'; 2 | 3 | export default { 4 | config: { 5 | locales: [ 6 | // 'ar', 7 | // 'fr', 8 | // 'cs', 9 | // 'de', 10 | // 'dk', 11 | // 'es', 12 | // 'he', 13 | // 'id', 14 | // 'it', 15 | // 'ja', 16 | // 'ko', 17 | // 'ms', 18 | // 'nl', 19 | // 'no', 20 | // 'pl', 21 | // 'pt-BR', 22 | // 'pt', 23 | // 'ru', 24 | // 'sk', 25 | // 'sv', 26 | // 'th', 27 | // 'tr', 28 | // 'uk', 29 | // 'vi', 30 | // 'zh-Hans', 31 | // 'zh', 32 | ], 33 | }, 34 | bootstrap(app: StrapiApp) {}, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/docs/blog/authors.yml: -------------------------------------------------------------------------------- 1 | yangshun: 2 | name: Yangshun Tay 3 | title: Front End Engineer @ Facebook 4 | url: https://github.com/yangshun 5 | image_url: https://github.com/yangshun.png 6 | page: true 7 | socials: 8 | x: yangshunz 9 | github: yangshun 10 | 11 | slorber: 12 | name: Sébastien Lorber 13 | title: Docusaurus maintainer 14 | url: https://sebastienlorber.com 15 | image_url: https://github.com/slorber.png 16 | page: 17 | # customize the url of the author page at /blog/authors/ 18 | permalink: '/all-sebastien-lorber-articles' 19 | socials: 20 | x: sebastienlorber 21 | linkedin: sebastienlorber 22 | github: slorber 23 | newsletter: https://thisweekinreact.com 24 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/configuration/xsl.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'XSL' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/xsl 5 | --- 6 | 7 | # XSL 8 | 9 | This plugin ships with some XSL files to make your sitemaps human readable. It adds some styling and does some reordering of the links. 10 | 11 | These changes are by no means a requirement for your sitemap to be valid. It is really just there to make your sitemap look pretty. 12 | 13 | If you have a large sitemap you might encounter performance issues when accessing the sitemap.xml from the browser. In that case you can disable the XSL to fix these issues. 14 | 15 | ###### Key: `xsl ` 16 | 17 | > `required:` NO | `type:` bool | `default:` true 18 | -------------------------------------------------------------------------------- /packages/core/admin/helpers/displayedFilters.ts: -------------------------------------------------------------------------------- 1 | const displayedFilters = [ 2 | { 3 | name: 'createdAt', 4 | fieldSchema: { 5 | type: 'date', 6 | }, 7 | metadatas: { label: 'createdAt' }, 8 | }, 9 | { 10 | name: 'updatedAt', 11 | fieldSchema: { 12 | type: 'date', 13 | }, 14 | metadatas: { label: 'updatedAt' }, 15 | }, 16 | { 17 | name: 'mime', 18 | fieldSchema: { 19 | type: 'enumeration', 20 | options: [ 21 | { label: 'image', value: 'image' }, 22 | { label: 'video', value: 'video' }, 23 | { label: 'file', value: 'file' }, 24 | ], 25 | }, 26 | metadatas: { label: 'type' }, 27 | }, 28 | ]; 29 | 30 | export default displayedFilters; 31 | -------------------------------------------------------------------------------- /playground/src/api/category/content-types/category/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "categories", 4 | "info": { 5 | "singularName": "category", 6 | "pluralName": "categories", 7 | "displayName": "Category", 8 | "description": "" 9 | }, 10 | "options": { 11 | "draftAndPublish": true 12 | }, 13 | "pluginOptions": { 14 | "webtools": { 15 | "enabled": true 16 | } 17 | }, 18 | "attributes": { 19 | "test": { 20 | "type": "relation", 21 | "relation": "oneToOne", 22 | "target": "api::test.test", 23 | "inversedBy": "category" 24 | }, 25 | "title": { 26 | "type": "string", 27 | "pluginOptions": {} 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/settings/hostname-overrides.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Hostname overrides' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/settings/hostname-overrides 5 | --- 6 | 7 | # Hostname overrides 8 | 9 | If you are using this plugin in a multilingual Strapi project you will be presented with a 'Hostname overrides' setting. 10 | With this setting you can set a specific hostname per language. 11 | 12 | This is handy for when you have a URL structure like this: 13 | 14 | - https://en.domain.com (english domain) 15 | - https://nl.domain.com (dutch domain) 16 | - https://de.domain.com (german domain) 17 | 18 | ###### Key: `hostname_overrides` 19 | 20 | > `required:` NO | `type:` object | `default:` {} 21 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/getting-started/sitemap-index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Sitemap index' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/sitemap-index 5 | --- 6 | 7 | # 🔗 Sitemap index 8 | 9 | Large sitemaps (larger then 45.000 urls) will automatically be split up in to seperate sitemaps.
10 | A sitemap index will be created that links to all the different sitemap chunks.
11 | That sitemap index will be accessible on the default `/api/sitemap/index.xml` location. 12 | 13 | It is required to set the `url` in the `./config/server.js` file in your Strapi installation. 14 | That will be used to create the links to the different chunks. 15 | 16 | You can alter the 45.000 magic number through plugin config. 17 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_media.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /playground/src/api/private-category/content-types/private-category/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "private_categories", 4 | "info": { 5 | "singularName": "private-category", 6 | "pluralName": "private-categories", 7 | "displayName": "Private category", 8 | "description": "" 9 | }, 10 | "options": { 11 | "draftAndPublish": true 12 | }, 13 | "pluginOptions": { 14 | "webtools": { 15 | "enabled": true 16 | } 17 | }, 18 | "attributes": { 19 | "title": { 20 | "type": "string" 21 | }, 22 | "test": { 23 | "type": "relation", 24 | "relation": "oneToOne", 25 | "target": "api::test.test", 26 | "mappedBy": "private_category" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/cli/src/commands/license-setup.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { checkStrapiProject } from '../utils/strapi'; 3 | import { logger } from '../utils/logger'; 4 | import { licenseSetup } from './subcommands/license-setup'; 5 | 6 | export async function setupLicense() { 7 | // Check if we're in a Strapi project 8 | const isStrapiProject = await checkStrapiProject(); 9 | if (!isStrapiProject) { 10 | console.log(chalk.red('Error: This command must be run in a Strapi project directory.')); 11 | return; 12 | } 13 | 14 | logger.title( 15 | 'Webtools', 16 | `${chalk.bold('🚀 Let\'s build your new website with Strapi')}\n`, 17 | ); 18 | 19 | await licenseSetup(); 20 | 21 | console.log(chalk.green('\nLicense setup complete!')); 22 | } 23 | -------------------------------------------------------------------------------- /packages/docs/docs/configuration/unique-per-locale.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Unique alias per locale' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /configuration/unique-per-locale 5 | --- 6 | 7 | # Unique URL alias per locale 8 | 9 | By default Webtools generates URL aliases that are unique within entire Strapi database. 10 | When you use a separate domain name per locale this prevents reusing the same alias between locales. 11 | 12 | Set `unique_per_locale` to `true` to allow Webtools to generate the same alias as long as the locale is different. 13 | 14 | | Name | Details | 15 | | ---- |---------------------| 16 | | Key | `unique_per_locale` | 17 | | Required | false | 18 | | Type | boolean | 19 | | Default | false | 20 | -------------------------------------------------------------------------------- /packages/core/packup.config.ts: -------------------------------------------------------------------------------- 1 | import { Config, defineConfig } from '@strapi/pack-up'; 2 | 3 | const config: Config = defineConfig({ 4 | bundles: [ 5 | { 6 | source: './admin/index.ts', 7 | import: './dist/admin/index.mjs', 8 | require: './dist/admin/index.js', 9 | runtime: 'web', 10 | }, 11 | { 12 | source: './server/index.ts', 13 | import: './dist/server/index.mjs', 14 | require: './dist/server/index.js', 15 | runtime: 'node', 16 | }, 17 | ], 18 | dist: './dist', 19 | /** 20 | * Because we're exporting a server & client package 21 | * which have different runtimes we want to ignore 22 | * what they look like in the package.json 23 | */ 24 | exports: {}, 25 | }); 26 | 27 | export default config; 28 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | ### What does it do? 13 | 14 | Describe the technical changes you did. 15 | 16 | ### Why is it needed? 17 | 18 | Describe the issue you are solving. 19 | 20 | ### How to test it? 21 | 22 | Provide information about the environment and the path to verify the behaviour. 23 | 24 | ### Related issue(s)/PR(s) 25 | 26 | Let us know if this is related to any issue/pull request 27 | -------------------------------------------------------------------------------- /packages/core/admin/components/WebtoolsPanel/Permalink/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { getFetchClient } from '@strapi/strapi/admin'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import CopyLinkButton from '../../CopyLinkButton'; 6 | import { Config } from '../../../../server/config'; 7 | 8 | interface Props { 9 | path: string 10 | } 11 | 12 | const EditViewRightLinks: React.FC = ({ path }) => { 13 | const { get } = getFetchClient(); 14 | const config = useQuery('config', async () => get('/webtools/info/config')); 15 | 16 | if (config.isLoading || config.isError || !config.data.data.website_url) return null; 17 | 18 | return ; 19 | }; 20 | 21 | export default EditViewRightLinks; 22 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/CrossCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .yarnrc 6 | .pnp 7 | .pnp.js 8 | 9 | # yarn 10 | .pnp.* 11 | .yarn/* 12 | !.yarn/patches 13 | !.yarn/plugins 14 | !.yarn/releases 15 | !.yarn/sdks 16 | !.yarn/versions 17 | 18 | # yalc 19 | .yalc 20 | yalc.lock 21 | 22 | # testing 23 | coverage 24 | 25 | # next.js 26 | .next/ 27 | out/ 28 | build 29 | 30 | # misc 31 | .DS_Store 32 | *.pem 33 | 34 | # debug 35 | npm-debug.log* 36 | yarn-debug.log* 37 | yarn-error.log* 38 | .pnpm-debug.log* 39 | 40 | # local env files 41 | .env.local 42 | .env.development.local 43 | .env.test.local 44 | .env.production.local 45 | 46 | # turbo 47 | .turbo 48 | 49 | # Cypress 50 | cypress/screenshots/ 51 | cypress/videos/ 52 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/duplicate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/users.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/docs/configuration/website-url.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Website URL' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /configuration/website-url 5 | --- 6 | 7 | # Website URL 8 | 9 | The website URL config is a handy tool that can be used to add a `copy permalink` button to the sidebar of the edit view. When Webtools knows what the absolute URL of your website is, we can combine that with the URL alias to get you your permalinks. 10 | 11 | :::note 12 | This setting is part of the core Webtools configuration and is not the same as the Sitemap addon "Hostname" setting. For Sitemap hostname, see [Hostname](/webtools/addons/sitemap/settings/hostname). 13 | ::: 14 | 15 | | Name | Details | 16 | | ---- | ------- | 17 | | Key | `website_url` | 18 | | Required | false | 19 | | Type | string | 20 | | Default | null | 21 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/Duplicate copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/docs/docs/configuration/default-pattern.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Default pattern' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /configuration/default-pattern 5 | --- 6 | 7 | # Default pattern 8 | 9 | Webtools allows you to create custom URL patterns, specifically tweaked to your requirements. The default pattern acts as a fallback, for when you have not set an URL pattern for a specific content type. This fallback will then be used to generate the URLs for this content type. 10 | 11 | For a quick conceptual overview, see [URL pattern](/webtools/url-pattern). For path formatting rules, see [Slugify](/webtools/configuration/slugify). 12 | 13 | | Name | Details | 14 | | ---- | ------- | 15 | | Key | `default_pattern` | 16 | | Required | false | 17 | | Type | string | 18 | | Default | `/[pluralName]/[documentId]` | 19 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | require('cypress-terminal-report/src/installLogsCollector')(); 20 | 21 | // Alternatively you can use CommonJS syntax: 22 | // require('./commands') 23 | -------------------------------------------------------------------------------- /packages/core/server/content-types/url-pattern/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "wt_url_patterns", 4 | "info": { 5 | "singularName": "url-pattern", 6 | "pluralName": "url-patterns", 7 | "displayName": "url-pattern" 8 | }, 9 | "options": { 10 | "draftAndPublish": false, 11 | "comment": "" 12 | }, 13 | "pluginOptions": { 14 | "content-manager": { 15 | "visible": false 16 | }, 17 | "content-type-builder": { 18 | "visible": false 19 | } 20 | }, 21 | "attributes": { 22 | "pattern": { 23 | "type": "string", 24 | "required": true 25 | }, 26 | "contenttype": { 27 | "type": "string", 28 | "required": true 29 | }, 30 | "languages": { 31 | "type": "json", 32 | "required": true 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/server/services/get-main-field.ts: -------------------------------------------------------------------------------- 1 | import { UID } from '@strapi/strapi'; 2 | 3 | interface ContentManagerConfig { 4 | settings?: { 5 | mainField?: string; 6 | }; 7 | } 8 | 9 | export const getMainField = async (uid: UID.ContentType): Promise => { 10 | const coreStoreSettings = (await strapi 11 | // TODO use documents service instead of any 12 | .query('strapi::core-store' as UID.Schema) 13 | .findMany({ 14 | where: { key: `plugin_content_manager_configuration_content_types::${uid}` }, 15 | })) as Array<{ value: string }>; 16 | 17 | if (!coreStoreSettings?.[0]) return null; 18 | 19 | const value = JSON.parse(coreStoreSettings[0].value) as ContentManagerConfig; 20 | 21 | return value?.settings?.mainField ?? null; 22 | }; 23 | 24 | export default () => ({ 25 | getMainField, 26 | }); 27 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_boolean.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/official-market.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/cli/src/commands/enable.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { checkStrapiProject } from '../utils/strapi'; 3 | import { getContentTypes } from '../utils/content-types'; 4 | import { enableContentTypes } from './subcommands/enable-content-types'; 5 | 6 | export async function enable() { 7 | // Check if we're in a Strapi project 8 | const isStrapiProject = await checkStrapiProject(); 9 | if (!isStrapiProject) { 10 | console.log(chalk.red('Error: This command must be run in a Strapi project directory.')); 11 | return; 12 | } 13 | 14 | // Get available content types 15 | const contentTypes = getContentTypes(); 16 | 17 | // Enable Webtools for content types 18 | await enableContentTypes(contentTypes); 19 | 20 | console.log(chalk.yellow('\nYou may need to restart your Strapi server for changes to take effect.')); 21 | } 22 | -------------------------------------------------------------------------------- /packages/cli/src/utils/strapi.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | 4 | interface PackageJson { 5 | dependencies?: { 6 | strapi?: string; 7 | '@strapi/strapi'?: string; 8 | [key: string]: string | undefined; 9 | }; 10 | } 11 | 12 | export async function checkStrapiProject(): Promise { 13 | try { 14 | // Check for package.json 15 | const packageJsonPath = path.join(process.cwd(), 'package.json'); 16 | if (!await fs.pathExists(packageJsonPath)) { 17 | return false; 18 | } 19 | 20 | const packageJson = await fs.readJson(packageJsonPath) as PackageJson; 21 | 22 | // Check if this is a Strapi project 23 | return packageJson.dependencies?.strapi !== undefined || 24 | packageJson.dependencies?.['@strapi/strapi'] !== undefined; 25 | } catch (error) { 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/admin/permissions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @todo 3 | * Change the permission keys to be single words. 4 | * That way the permissions work better wit the useRBAC hook. 5 | */ 6 | const pluginPermissions = { 7 | // This permission regards the main component (App) and is used to tell 8 | // If the plugin link should be displayed in the menu 9 | // And also if the plugin is accessible. This use case is found when a user types the url of the 10 | // plugin directly in the browser 11 | 'settings.list': [{ action: 'plugin::webtools.settings.list', subject: null }], 12 | 'settings.overview': [{ action: 'plugin::webtools.settings.overview', subject: null }], 13 | 'settings.patterns': [{ action: 'plugin::webtools.settings.patterns', subject: null }], 14 | 'edit-view.sidebar': [{ action: 'plugin::webtools.edit-view.sidebar', subject: null }], 15 | }; 16 | 17 | export default pluginPermissions; 18 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/getting-started/cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'CLI' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/cli 5 | --- 6 | 7 | # 📺 CLI 8 | 9 | This addon includes its own `strapi-sitemap` CLI. This is separate from the core Webtools CLI used during installation/enabling. For core Webtools installation and enabling content types, see the [Installation guide](/webtools/). 10 | 11 | You can add it to your project like so: 12 | 13 | ``` 14 | "scripts": { 15 | // ... 16 | "sitemap": "strapi-sitemap" 17 | }, 18 | ``` 19 | 20 | You can now run the `generate` command like so: 21 | 22 | 23 | 24 | ``` 25 | yarn sitemap generate 26 | ``` 27 | 28 | 29 | ``` 30 | npm run sitemap generate 31 | ``` 32 | 33 | 34 | -------------------------------------------------------------------------------- /packages/docs/src/components/Hero/Hero.jsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import React from 'react'; 3 | import styles from './hero.module.scss'; 4 | 5 | export function HeroTitle({ 6 | className, 7 | ...rest 8 | }) { 9 | return ( 10 |

17 | ); 18 | } 19 | 20 | export function HeroDescription({ 21 | className, 22 | ...rest 23 | }) { 24 | return ( 25 |
32 | ); 33 | } 34 | 35 | export function Hero({ 36 | className, 37 | ...rest 38 | }) { 39 | return ( 40 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/external_link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/docs/getting-started/url-alias.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'URL alias' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /url-alias 5 | --- 6 | 7 | import ThemedImage from '@theme/ThemedImage'; 8 | import useBaseUrl from '@docusaurus/useBaseUrl'; 9 | 10 | # 💡 URL alias 11 | At the core of the plugin is URL alias. It's the idea that every page of a given collection type represents a page in your website frontend. Each of those pages will have a unique URL path which can be altered on the backend. Then all those URL paths can be used in your frontend to setup dynamic routing. 12 | 13 | See below a screenshot of the URL alias popup that's used to set the unique path of your page. 14 | 15 | 22 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_date.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/helpers/timeFormat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Make a time string double digit. So make 9 in to 09. 3 | * 4 | * @param {int} number - The number. 5 | * 6 | * @returns {int} The double digit number. 7 | */ 8 | const doubleDigits = (number) => { 9 | return (`0${number}`).slice(-2); 10 | }; 11 | 12 | /** 13 | * Format a timestamp to hh:mm:ss. 14 | * 15 | * @param {int} timestamp - The unix timestamp. 16 | * @param {bool} withSeconds - Whether to include the seconds. 17 | * 18 | * @returns {string} The formatted time. 19 | */ 20 | export const formatTime = (timestamp, withSeconds = false) => { 21 | const dateObj = new Date(timestamp); 22 | const hours = doubleDigits(dateObj.getHours()); 23 | const minutes = doubleDigits(dateObj.getMinutes()); 24 | const seconds = doubleDigits(dateObj.getSeconds()); 25 | 26 | return `${hours}:${minutes}${withSeconds ? `:${seconds}` : ''}`; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/addons/sitemap/admin/translations/index.js: -------------------------------------------------------------------------------- 1 | import ar from './ar.json'; 2 | import de from './de.json'; 3 | import en from './en.json'; 4 | import es from './es.json'; 5 | import fr from './fr.json'; 6 | import it from './it.json'; 7 | import ja from './ja.json'; 8 | import ko from './ko.json'; 9 | import nl from './nl.json'; 10 | import pl from './pl.json'; 11 | import ptBR from './pt-BR.json'; 12 | import pt from './pt.json'; 13 | import ru from './ru.json'; 14 | import tr from './tr.json'; 15 | import uk from './uk.json'; 16 | import vi from './vi.json'; 17 | import zhHans from './zh-Hans.json'; 18 | import zh from './zh.json'; 19 | 20 | const trads = { 21 | ar, 22 | de, 23 | en, 24 | es, 25 | fr, 26 | it, 27 | ja, 28 | ko, 29 | nl, 30 | pl, 31 | 'pt-BR': ptBR, 32 | pt, 33 | ru, 34 | tr, 35 | uk, 36 | vi, 37 | 'zh-Hans': zhHans, 38 | zh, 39 | }; 40 | 41 | export default trads; 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "target": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "strict": false, 7 | "skipLibCheck": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "incremental": true, 10 | "esModuleInterop": true, 11 | "resolveJsonModule": true, 12 | "noEmitOnError": true, 13 | "noImplicitThis": true, 14 | "allowJs": true, 15 | "outDir": "dist", 16 | "rootDir": ".", 17 | "jsx": "react-jsx", 18 | "typeRoots": [ 19 | "node_modules/@types", 20 | "types", 21 | "server/strapi.d.ts" 22 | ], 23 | }, 24 | "include": [ 25 | "**/*.ts", 26 | "**/*.tsx" 27 | ], 28 | "exclude": [ 29 | "**/node_modules/**/*", 30 | "**/build/**/*", 31 | "**/__tests__/**/*", 32 | "**/*.cy.*", 33 | "**/*.test.*", 34 | "./cypress/**/*", 35 | "./*.config.*" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/addons/sitemap/xsl/sortable.min.js: -------------------------------------------------------------------------------- 1 | var document = document || null; 2 | if (document) { document.addEventListener('click', (b) => { function n(a, e) { a.className = a.className.replace(u, '') + e; } function p(a) { return a.getAttribute('data-sort') || a.innerText; } var u = / dir-(u|d) /; let c = /\bsortable\b/; b = b.target; if (b.nodeName === 'TH') { try { let q = b.parentNode; let f = q.parentNode.parentNode; if (c.test(f.className)) { let g; let d = q.cells; for (c = 0; c < d.length; c++)d[c] === b ? g = c : n(d[c], ''); d = ' dir-d '; b.className.indexOf(' dir-d ') !== -1 && (d = ' dir-u '); n(b, d); let h = f.tBodies[0]; let k = [].slice.call(h.rows, 0); let r = d === ' dir-u '; k.sort((a, 3 | e) => { let l = p((r ? a : e).cells[g]); let m = p((r ? e : a).cells[g]); return isNaN(l - m) ? l.localeCompare(m) : l - m; }); for (var t = h.cloneNode(); k.length;)t.appendChild(k.splice(0, 1)[0]); f.replaceChild(t, h); } } catch (a) {} } }); } 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/api_tokens.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_password.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/docs/src/scss/custom-doc-cards.scss: -------------------------------------------------------------------------------- 1 | /** Component: CustomDocCardWrapper */ 2 | 3 | .custom-cards-wrapper { 4 | display: flex; 5 | flex-flow: row wrap; 6 | max-width: 715px; 7 | justify-content: flex-start; 8 | 9 | .custom-doc-card { 10 | max-width: 320px; 11 | width: 320px; 12 | margin-bottom: 10px; 13 | margin-right: 20px; 14 | 15 | a { 16 | height: 170px; 17 | } 18 | 19 | } 20 | } 21 | 22 | /** Component: CustomDocCard */ 23 | .custom-doc-card { 24 | 25 | a { 26 | text-decoration: none; 27 | } 28 | 29 | a:hover { 30 | .cardTitle { 31 | color: var(--strapi-primary-600); 32 | } 33 | } 34 | 35 | .text--truncate.cardDescription { 36 | // cancel --truncate styles since we can't remove the' class 37 | overflow: auto; 38 | white-space: normal; 39 | } 40 | } 41 | 42 | .custom-doc-card--small { 43 | .cardTitle { 44 | margin-bottom: 0; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/addons/sitemap/packup.config.ts: -------------------------------------------------------------------------------- 1 | import { Config, defineConfig } from '@strapi/pack-up'; 2 | 3 | const config: Config = defineConfig({ 4 | bundles: [ 5 | { 6 | source: './admin/index.js', 7 | import: './dist/admin/index.mjs', 8 | require: './dist/admin/index.js', 9 | runtime: 'web', 10 | }, 11 | { 12 | source: './server/index.js', 13 | import: './dist/server/index.mjs', 14 | require: './dist/server/index.js', 15 | runtime: 'node', 16 | }, 17 | { 18 | source: './server/cli.js', 19 | import: './dist/cli/index.mjs', 20 | require: './dist/cli/index.js', 21 | runtime: 'node', 22 | }, 23 | ], 24 | dist: './dist', 25 | /** 26 | * Because we're exporting a server & client package 27 | * which have different runtimes we want to ignore 28 | * what they look like in the package.json 29 | */ 30 | exports: {}, 31 | }); 32 | 33 | export default config; 34 | -------------------------------------------------------------------------------- /packages/docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/roles_permissions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/src/components/LinkWithArrow/LinkWithArrow.jsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import React from 'react'; 3 | import Link from '@docusaurus/Link'; 4 | import IconArrow from '@site/static/webtools/img/assets/icons/arrow-right.svg'; 5 | import styles from './link-with-arrow.module.scss'; 6 | 7 | export function LinkWithArrow({ 8 | apart = false, 9 | children, 10 | className, 11 | href, 12 | to, 13 | ...rest 14 | }) { 15 | const LinkElement = (to ? Link : 'a'); 16 | 17 | return ( 18 | 28 | 29 | {children} 30 | 31 | 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout Repo 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup Node.js 20 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 20 22 | 23 | - name: Install Dependencies 24 | run: yarn 25 | 26 | - name: Create Release Pull Request 27 | uses: changesets/action@v1 28 | with: 29 | # This expects you to have a script called release which does a build for your packages and calls changeset publish 30 | publish: yarn release:publish 31 | version: yarn release:prepare 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.PLUGINPAL_BOT_GITHUB_TOKEN }} 34 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 35 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/media_library.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | -------------------------------------------------------------------------------- /packages/docs/blog/2021-08-26-welcome/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: welcome 3 | title: Welcome 4 | authors: [slorber, yangshun] 5 | tags: [facebook, hello, docusaurus] 6 | --- 7 | 8 | [Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). 9 | 10 | Here are a few tips you might find useful. 11 | 12 | 13 | 14 | Simply add Markdown files (or folders) to the `blog` directory. 15 | 16 | Regular blog authors can be added to `authors.yml`. 17 | 18 | The blog post date can be extracted from filenames, such as: 19 | 20 | - `2019-05-30-welcome.md` 21 | - `2019-05-30-welcome/index.md` 22 | 23 | A blog post folder can be convenient to co-locate blog post images: 24 | 25 | ![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) 26 | 27 | The blog supports tags as well! 28 | 29 | **And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. 30 | -------------------------------------------------------------------------------- /packages/docs/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # Stage 1: Base image. 4 | ## Start with a base image containing NodeJS so we can build Docusaurus. 5 | FROM node:20-alpine as base 6 | ## Disable colour output from yarn to make logs easier to read. 7 | ENV FORCE_COLOR=0 8 | ## Enable corepack. 9 | RUN corepack enable 10 | ## Set the working directory to `/opt/docusaurus`. 11 | WORKDIR /opt/docusaurus 12 | 13 | # Stage 2b: Production build mode. 14 | FROM base as prod 15 | ## Set the working directory to `/opt/docusaurus`. 16 | WORKDIR /opt/docusaurus 17 | ## Copy over the source code. 18 | COPY . /opt/docusaurus/ 19 | ## Install dependencies with `--immutable` to ensure reproducibility. 20 | RUN yarn install 21 | ## Build the static site. 22 | RUN yarn build 23 | 24 | # Stage 3a: Serve with `docusaurus serve`. 25 | FROM prod as serve 26 | ## Expose the port that Docusaurus will run on. 27 | EXPOSE 3000 28 | ## Run the production server. 29 | CMD ["yarn", "serve", "--host", "0.0.0.0", "--no-open"] 30 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/configuration/auto-generate.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Auto generate' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/configuration/auto-generate 5 | --- 6 | 7 | # Auto generate 8 | 9 | Alternatively to using cron to regenerate your sitemap, this plugin offers an automatic generation feature that will generate the sitemap as a document service middleware. On `create`, `update` and `delete` this plugin will do a full sitemap regeneration. This way your sitemap will always be up-to-date when making content changes. 10 | 11 | If you have a large sitemap the regeneration becomes an expensive task. Because of that this setting is disabled by default and it is not recommended to enable it for sitemaps with more than 1000 links. 12 | 13 | Also the search engines don't even crawl your sitemap that often, so generating it once a day through cron should be suffecient. 14 | 15 | ###### Key: `autoGenerate ` 16 | 17 | > `required:` NO | `type:` bool | `default:` false 18 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/roles.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/core/admin/screens/List/components/Filters/FilterInput/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 3 | import React from 'react'; 4 | import { SingleSelect, SingleSelectOption } from '@strapi/design-system'; 5 | import { Filters, useField } from '@strapi/admin/strapi-admin'; 6 | 7 | const FilterInput = (props: Filters.ValueInputProps) => { 8 | const { name, options } = props; 9 | 10 | const field = useField(name); 11 | 12 | const handleChange = (value?: string) => { 13 | field.onChange(name, value); 14 | }; 15 | 16 | return ( 17 | 18 | {options.map((contentType) => ( 19 | 20 | {contentType.label} 21 | 22 | ))} 23 | 24 | ); 25 | }; 26 | 27 | export default FilterInput; 28 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/Browsers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/src/scss/tabs.scss: -------------------------------------------------------------------------------- 1 | /** Component: Tab */ 2 | 3 | :root body { 4 | --custom-tabs-px: var(--strapi-spacing-5); 5 | --custom-tabs-py: var(--strapi-spacing-2); 6 | 7 | --ifm-tabs-padding-horizontal: var(--custom-tabs-px); 8 | --ifm-tabs-padding-vertical: var(--custom-tabs-py); 9 | } 10 | 11 | .tabs { 12 | &__item { 13 | border-top: 2px solid transparent; 14 | } 15 | 16 | /** Tabs inside Tabs */ 17 | + div { 18 | [role="tabpanel"] { 19 | .tabs { 20 | font-size: var(--strapi-font-size-ssm); 21 | 22 | &__item { 23 | &--active { 24 | --ifm-tabs-color-active-border: transparent; 25 | 26 | background-color: var(--ifm-hover-overlay); 27 | } 28 | } 29 | 30 | + [class*="margin-top"] { 31 | margin-top: 0 !important; 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | /** Tabs inside Details component */ 39 | details { 40 | .tabs { 41 | --ifm-tabs-color-active-border: var(--strapi-) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/plugins.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 PluginPal. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /packages/docs/src/components/Button/Button.jsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import React from 'react'; 3 | import Link from '@docusaurus/Link'; 4 | import styles from './button.module.scss'; 5 | 6 | export function Button({ 7 | href, 8 | to, 9 | children, 10 | className, 11 | decorative, 12 | size = '', 13 | variant = 'primary', 14 | ...rest 15 | }) { 16 | const ButtonElement = (to ? Link : (href ? 'a' : 'button')); 17 | 18 | return ( 19 | 32 | {children} 33 | {decorative && ( 34 | 35 | {decorative} 36 | 37 | )} 38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /packages/docs/src/components/CustomDocCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classNames from 'classnames'; 3 | 4 | export default function CustomDocCard(props) { 5 | const { title, description, link, emoji, small = false } = props; 6 | const linkClasses = classNames({ 7 | card: true, 8 | cardContainer: true, 9 | 'padding--lg': !small, 10 | 'padding--md': small, 11 | }); 12 | const cardClasses = classNames({ 13 | 'custom-doc-card': true, 14 | 'margin-bottom--lg': !small, 15 | 'margin-bottom--sm': small, 16 | 'custom-doc-card--small': small, 17 | }); 18 | return ( 19 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /packages/cli/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 PluginPal. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /packages/core/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 PluginPal. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /packages/docs/src/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | /** Core: Sass Mixins */ 2 | 3 | /** Mixin: Responsive */ 4 | @mixin small-up { 5 | @media (min-width: 769px) { 6 | @content; 7 | } 8 | } 9 | 10 | /** Mixin: Responsive */ 11 | @mixin medium-up { 12 | @media (min-width: 997px) { 13 | @content; 14 | } 15 | } 16 | 17 | /** Mixin: Responsive */ 18 | @mixin large-up { 19 | @media (min-width: 1536px) { 20 | @content; 21 | } 22 | } 23 | 24 | /** Mixin: Dark mode */ 25 | @mixin dark { 26 | html[data-theme='dark'] { 27 | @content; 28 | } 29 | } 30 | 31 | /** Mixin: Light mode */ 32 | @mixin light { 33 | html[data-theme='light'] { 34 | @content; 35 | } 36 | } 37 | 38 | /** Mixin: Transition */ 39 | @mixin transition { 40 | transition: all 0.2s ease; 41 | } 42 | 43 | /** Mixin: Flex */ 44 | @mixin flex-row( 45 | $gap: var(--strapi-spacing-2), 46 | $align-items: center, 47 | $justify-content: null 48 | ) { 49 | display: flex; 50 | flex-direction: row; 51 | gap: $gap; 52 | align-items: $align-items; 53 | justify-content: $justify-content; 54 | } 55 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_relation_1to1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/addons/sitemap/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 PluginPal. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/register.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import _ from 'lodash'; 5 | import { isContentTypeEnabled } from './utils/enabledContentTypes'; 6 | import autoGenerateMiddleware from './middlewares/auto-generate'; 7 | 8 | /** 9 | * Adds sitemap_exclude field to all the eligable content types. 10 | * @param {Strapi} strapi - The Strapi instance. 11 | * 12 | * @returns {void} 13 | */ 14 | const extendContentTypesWithExcludeField = async (strapi) => { 15 | Object.values(strapi.contentTypes).forEach((contentType) => { 16 | const hasWT = isContentTypeEnabled(contentType); 17 | 18 | if (!hasWT) return; 19 | 20 | const { attributes } = contentType; 21 | 22 | _.set(attributes, 'sitemap_exclude', { 23 | writable: true, 24 | private: true, 25 | configurable: false, 26 | visible: false, 27 | default: false, 28 | type: 'boolean', 29 | }); 30 | }); 31 | }; 32 | 33 | export default ({ strapi }) => { 34 | strapi.documents.use(autoGenerateMiddleware); 35 | extendContentTypesWithExcludeField(strapi); 36 | }; 37 | -------------------------------------------------------------------------------- /playground/tests/helpers.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import assert from 'node:assert'; 3 | import { createStrapi } from '@strapi/strapi'; 4 | import type { Core } from '@strapi/types'; 5 | 6 | let instance: Core.Strapi | undefined; 7 | 8 | /** 9 | * Setups strapi for futher testing 10 | */ 11 | export async function setupStrapi() { 12 | if (!instance) { 13 | const strapi = await createStrapi({ 14 | appDir: './playground', 15 | distDir: './playground/dist', 16 | }).load(); 17 | 18 | await strapi.start(); 19 | 20 | instance = strapi; // strapi is global now 21 | } 22 | } 23 | 24 | /** 25 | * Closes strapi after testing 26 | */ 27 | export async function stopStrapi() { 28 | if (instance) { 29 | const tmpDbFile = instance.config.get( 30 | 'database.connection.connection.filename', 31 | ); 32 | 33 | assert(typeof tmpDbFile === 'string'); 34 | 35 | await instance.destroy(); 36 | 37 | if (fs.existsSync(tmpDbFile)) { 38 | fs.unlinkSync(tmpDbFile); 39 | } 40 | 41 | instance = undefined; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/content-types/sitemap/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "wt_sitemap", 4 | "info": { 5 | "singularName": "sitemap", 6 | "pluralName": "sitemaps", 7 | "displayName": "sitemap" 8 | }, 9 | "options": { 10 | "draftAndPublish": false 11 | }, 12 | "pluginOptions": { 13 | "content-manager": { 14 | "visible": false 15 | }, 16 | "content-type-builder": { 17 | "visible": false 18 | } 19 | }, 20 | "attributes": { 21 | "sitemap_string": { 22 | "type": "text", 23 | "required": true 24 | }, 25 | "name": { 26 | "type": "string", 27 | "default": "default", 28 | "required": true 29 | }, 30 | "type": { 31 | "type": "enumeration", 32 | "enum": [ 33 | "default_hreflang", 34 | "index" 35 | ], 36 | "default": "default_hreflang" 37 | }, 38 | "delta": { 39 | "type": "integer", 40 | "default": 1 41 | }, 42 | "link_count": { 43 | "type": "integer" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/content_types_builder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/docs/configuration/slugify.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Slugify' 3 | displayed_sidebar: webtoolsSidebar 4 | slug: /configuration/slugify 5 | --- 6 | 7 | # Slugify 8 | 9 | This config can be used to overwrite the function that is used to slugify your URLs. The function takes a single parameter which is the path before being slugified. It expects the return value to be the slugified URL. 10 | 11 | For an introduction to patterns and how slugs are derived, see [URL pattern](/webtools/url-pattern). For the fallback behavior, see [Default pattern](/webtools/configuration/default-pattern). 12 | 13 | ### Example: 14 | 15 | ```md title="config/plugins.js" 16 | import deburr from 'lodash/deburr'; 17 | import toLower from 'lodash/toLower'; 18 | import kebabCase from 'lodash/kebabCase'; 19 | 20 | export default ({ env }) => ({ 21 | webtools: { 22 | config: { 23 | slugify: (fieldValue) => kebabCase(deburr(toLower(fieldValue))), 24 | }, 25 | }, 26 | }); 27 | ``` 28 | 29 | | Name | Details | 30 | | ---- | ------- | 31 | | Key | `slugify` | 32 | | Required | false | 33 | | Type | function | 34 | -------------------------------------------------------------------------------- /packages/core/server/content-types/url-alias/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "wt_url_alias", 4 | "info": { 5 | "singularName": "url-alias", 6 | "pluralName": "url-alias", 7 | "displayName": "url-alias" 8 | }, 9 | "options": { 10 | "draftAndPublish": false, 11 | "comment": "" 12 | }, 13 | "pluginOptions": { 14 | "content-manager": { 15 | "visible": false 16 | }, 17 | "content-type-builder": { 18 | "visible": false 19 | }, 20 | "i18n": { 21 | "localized": true 22 | } 23 | }, 24 | "attributes": { 25 | "url_path": { 26 | "type": "string", 27 | "required": true, 28 | "pluginOptions": { 29 | "i18n": { 30 | "localized": true 31 | } 32 | } 33 | }, 34 | "generated": { 35 | "type": "boolean", 36 | "default": true, 37 | "pluginOptions": { 38 | "i18n": { 39 | "localized": true 40 | } 41 | } 42 | }, 43 | "contenttype": { 44 | "type": "string", 45 | "required": true 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_dz.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/utils/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export const getCoreStore = () => { 4 | return strapi.store({ type: 'plugin', name: 'webtools-addon-sitemap' }); 5 | }; 6 | 7 | export const logMessage = (msg = '') => `[webtools-addon-sitemap]: ${msg}`; 8 | 9 | export const noLimit = async (queryString, parameters, limit = 5000) => { 10 | let entries = []; 11 | const amountOfEntries = await strapi.documents(queryString).count(parameters); 12 | 13 | for (let i = 0; i < (amountOfEntries / limit); i++) { 14 | /* eslint-disable-next-line */ 15 | const chunk = await strapi.documents(queryString).findMany({ 16 | ...parameters, 17 | limit: limit, 18 | start: (i * limit), 19 | }); 20 | if (chunk.id) { 21 | entries = [chunk, ...entries]; 22 | } else { 23 | entries = [...chunk, ...entries]; 24 | } 25 | } 26 | 27 | return entries; 28 | }; 29 | 30 | export const isValidUrl = (url) => { 31 | try { 32 | // eslint-disable-next-line no-new 33 | new URL(url); 34 | return true; 35 | } catch (err) { 36 | return false; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /packages/cli/src/constants/addons.ts: -------------------------------------------------------------------------------- 1 | export interface Addon { 2 | name: string; 3 | description: string; 4 | packageName: string; 5 | } 6 | 7 | export const AVAILABLE_ADDONS: Addon[] = [ 8 | { 9 | name: 'sitemap', 10 | description: 'Generate a sitemap for your Strapi content', 11 | packageName: 'webtools-addon-sitemap', 12 | }, 13 | ]; 14 | 15 | export const PREMIUM_ADDONS: Addon[] = [ 16 | { 17 | name: 'redirects', 18 | description: 'Manage redirects for your Strapi content', 19 | packageName: '@pluginpal/webtools-addon-redirects', 20 | }, 21 | { 22 | name: 'links', 23 | description: 'Advanced link management for your Strapi content', 24 | packageName: '@pluginpal/webtools-addon-links', 25 | }, 26 | { 27 | name: 'breadcrumbs', 28 | description: 'Generated breadcrumbs based on your URL alias', 29 | packageName: '@pluginpal/webtools-addon-breadcrumbs', 30 | }, 31 | { 32 | name: 'sitemap expansion', 33 | description: 'Additional features for the sitemap addon', 34 | packageName: '@pluginpal/webtools-addon-sitemap-expansion', 35 | }, 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/CreditCard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/src/scss/__index.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. 3 | * The classic template bundles Infima by default. 4 | * Infima is a CSS framework designed to work well for content-centric websites. 5 | */ 6 | 7 | /** Core */ 8 | @import '_mixins.scss'; 9 | @import '_tokens.scss'; 10 | @import '_tokens-overrides.scss'; 11 | 12 | /** Base */ 13 | @import '_fonts.scss'; 14 | @import '_base.scss'; 15 | 16 | /** Components */ 17 | @import 'admonition.scss'; 18 | @import 'api-call.scss'; 19 | @import 'badge.scss'; 20 | @import 'breadcrumbs.scss'; 21 | @import 'card.scss'; 22 | @import 'columns.scss'; 23 | @import 'container.scss'; 24 | @import 'custom-doc-cards.scss'; 25 | @import 'details.scss'; 26 | @import 'footer.scss'; 27 | @import 'grid.scss'; 28 | @import 'images.scss'; 29 | @import 'markdown.scss'; 30 | @import 'medium-zoom.scss'; 31 | @import 'navbar.scss'; 32 | @import 'pagination-nav.scss'; 33 | @import 'search.scss'; 34 | @import 'scene.scss'; 35 | @import 'sidebar.scss'; 36 | @import 'table.scss'; 37 | @import 'tabs.scss'; 38 | @import 'table-of-contents.scss'; 39 | @import 'typography.scss'; 40 | -------------------------------------------------------------------------------- /packages/docs/src/scss/details.scss: -------------------------------------------------------------------------------- 1 | /** Component: Details/Accordion */ 2 | 3 | details.alert { 4 | --docusaurus-details-decoration-color: var(--strapi-neutral-800); 5 | 6 | --ifm-alert-background-color: var(--strapi-neutral-150); 7 | --ifm-alert-background-color-highlight: var(--strapi-neutral-500); 8 | --ifm-alert-border-radius: var(--strapi-spacing-1); 9 | --ifm-alert-foreground-color: var( --ifm-color-info-contrast-foreground ); 10 | --ifm-alert-border-color: transparent; 11 | 12 | --ifm-tabs-color-active: var(--ifm-color-primary); 13 | --ifm-tabs-color-active-border: var(--ifm-color-primary); 14 | 15 | summary { 16 | p { 17 | margin: 0; 18 | } 19 | } 20 | 21 | /** Content element */ 22 | > div > div { 23 | --docusaurus-details-decoration-color: transparent; 24 | 25 | margin-top: 0; 26 | } 27 | 28 | a { 29 | color: var(--custom-code-color); 30 | } 31 | 32 | a:hover { 33 | text-decoration: var(--ifm-link-decoration); 34 | } 35 | } 36 | 37 | @include dark { 38 | details a { 39 | color: var(--strapi-primary-500); 40 | font-weight: 700; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/ctb_relation_manyway.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/marketplace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/addons/sitemap/xsl/sitemap.xsl.js: -------------------------------------------------------------------------------- 1 | var document = document || null; 2 | if (document) { 3 | function formatChangeFreq(changeFreq) { 4 | switch (changeFreq) { 5 | case 'always': 6 | return 0; 7 | 8 | case 'hourly': 9 | return 1; 10 | 11 | case 'daily': 12 | return 2; 13 | 14 | case 'weekly': 15 | return 3; 16 | 17 | case 'monthly': 18 | return 4; 19 | 20 | case 'yearly': 21 | return 5; 22 | 23 | default: 24 | return 6; 25 | } 26 | } 27 | 28 | setTimeout(() => { 29 | // Set data-sort attribute for changefreq field. 30 | let tds = document.getElementsByClassName('changefreq'); 31 | for (let i = 0; i < tds.length; i++) { 32 | tds[i].setAttribute('data-sort', formatChangeFreq(tds[i].textContent)); 33 | } 34 | 35 | // Add URL to title. 36 | let h1 = document.getElementsByTagName('h1')[0]; 37 | h1.innerHTML = h1.textContent + ': ' + location; 38 | document.title = h1.textContent; 39 | 40 | // Set default sort. 41 | document.getElementById('default-sort').click(); 42 | }, 0); 43 | } 44 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "moduleResolution": "Node", 5 | "lib": ["ES2020"], 6 | "target": "ES2019", 7 | "strict": false, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "noEmitOnError": true, 14 | "noImplicitThis": true, 15 | "outDir": "dist", 16 | "rootDir": "." 17 | }, 18 | "include": [ 19 | // Include root files 20 | "./", 21 | // Include all ts files 22 | "./**/*.ts", 23 | // Include all js files 24 | "./**/*.js", 25 | // Force the JSON files in the src folder to be included 26 | "src/**/*.json" 27 | ], 28 | 29 | "exclude": [ 30 | "node_modules/", 31 | "build/", 32 | "dist/", 33 | ".cache/", 34 | ".tmp/", 35 | 36 | // Do not include admin files in the server compilation 37 | "src/admin/", 38 | // Do not include test files 39 | "**/*.test.*", 40 | // Do not include plugins in the server compilation 41 | "src/plugins/**" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /packages/core/server/middlewares/prevent-duplicate-urls.ts: -------------------------------------------------------------------------------- 1 | import { Modules } from '@strapi/strapi'; 2 | import { getPluginService } from '../util/getPluginService'; 3 | 4 | // eslint-disable-next-line max-len 5 | const preventDuplicateUrlsMiddleware: Modules.Documents.Middleware.Middleware = async (context, next) => { 6 | const { uid, action } = context; 7 | 8 | // Only run this for the URL alias entities. 9 | if (uid !== 'plugin::webtools.url-alias') { 10 | return next(); 11 | } 12 | 13 | // Run this middleware only for the create, update and clone action. 14 | if (!['create', 'update'].includes(action)) { 15 | return next(); 16 | } 17 | 18 | const params = context.params as Modules.Documents.ServiceParams<'plugin::webtools.url-alias'>['create' | 'update' | 'clone'] & { documentId: string }; 19 | 20 | if (params.data.url_path) { 21 | params.data.url_path = await getPluginService('url-alias') 22 | .makeUniquePath(params.data.url_path, action !== 'clone' && params.documentId, action !== 'clone' && params.locale); 23 | } 24 | 25 | return next(); 26 | }; 27 | 28 | export default preventDuplicateUrlsMiddleware; 29 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/sitemap/settings/default-language-url-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Default language URL type' 3 | displayed_sidebar: webtoolsSitemapSidebar 4 | slug: /addons/sitemap/settings/default-language-url-type 5 | --- 6 | 7 | # Default language URL (x-default) 8 | 9 | This setting will add an additionnal `` tag into each sitemap urls bundles with value `hreflang="x-default"` and the path of your choice. The hreflang x-default value is used to specify the language and region neutral URL for a piece of content when the site doesn't support the user's language and region. For example, if a page has hreflang annotations for English and Spanish versions of a page along with an x-default value pointing to the English version, French speaking users are sent to the English version of the page due to the x-default annotation. The x-default page can be a language and country selector page, the page where you redirect users when you have no content for their region, or just the version of the content that you consider default. 10 | 11 | ###### Key: `defaultLanguageUrlType` 12 | 13 | > `required:` NO | `type:` string | `default:` '' 14 | -------------------------------------------------------------------------------- /packages/docs/src/scss/card.scss: -------------------------------------------------------------------------------- 1 | /** Component: Card */ 2 | 3 | :root body { 4 | --custom-card-border-color: var(--strapi-neutral-200); 5 | 6 | --ifm-card-background-color: var(--strapi-neutral-0); 7 | --ifm-card-border-radius: var(--strapi-spacing-1); 8 | } 9 | 10 | .card { 11 | --ifm-color-emphasis-200: var(--custom-card-border-color); 12 | 13 | border: 1px solid var(--custom-card-border-color); 14 | box-shadow: 0px 1px 4px rgba(33, 33, 52, 0.1) !important; 15 | @include transition; 16 | 17 | &[href] { 18 | color: var(--strapi-neutral-600); 19 | 20 | &:hover { 21 | --ifm-color-primary: var(--strapi-neutral-300); 22 | 23 | box-shadow: 0px 2px 15px rgba(33, 33, 52, 0.1) !important; 24 | } 25 | } 26 | 27 | h2 { 28 | --ifm-h2-font-size: var(--strapi-font-size-md); 29 | margin-bottom: var(--strapi-spacing-2); 30 | } 31 | 32 | p { 33 | font-size: var(--strapi-font-size-sm); 34 | 35 | &:last-of-type { 36 | margin-bottom: 0; 37 | } 38 | } 39 | } 40 | 41 | /** Dark mode */ 42 | @include dark { 43 | .card { 44 | &[href] { 45 | color: var(--strapi-neutral-700); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/core/admin/screens/404/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { EmptyStateLayout, LinkButton } from '@strapi/design-system'; 4 | import { Layouts } from '@strapi/strapi/admin'; 5 | 6 | import { EmptyDocuments } from '@strapi/icons/symbols'; 7 | import { ArrowRight } from '@strapi/icons'; 8 | import { Link } from 'react-router-dom'; 9 | import { useIntl } from 'react-intl'; 10 | 11 | const PageNotFound = () => { 12 | const { formatMessage } = useIntl(); 13 | 14 | return ( 15 | <> 16 | 19 | 20 | } 23 | action={}>Back to homepage} 24 | shadow="tableShadow" 25 | hasRadius 26 | /> 27 | 28 | 29 | ); 30 | }; 31 | 32 | export default PageNotFound; 33 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/links/getting-started/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Introduction' 3 | displayed_sidebar: webtoolsLinksSidebar 4 | slug: /addons/links 5 | --- 6 | 7 | # Webtools Links Add-on 8 | 9 | The **Links add-on** for Webtools Pro provides a powerful custom field type that allows content creators to easily create internal links within their Strapi CMS content. This field type streamlines the process of linking to other content entries, pages, or sections within your application. 10 | 11 | :::note 12 | This plugin acts as an extension of the core `strapi-plugin-webtools`. Please install and configure that before proceeding. 13 | ::: 14 | 15 | ## Key Features 16 | 17 | - **🔗 Internal Link Management** - Easy selection of internal content for linking 18 | - **⚡ Custom field** - Add the link field to any content type 19 | - **⚡ Ckeditor integration** - Integrate the dynamic links in to your ckeditor 20 | - **🔍 Smart Search** - Find content quickly with built-in search functionality 21 | - **📝 Content Type Support** - Works with all your custom content types 22 | - **🎨 Intuitive UI** - Clean, user-friendly interface integrated with Strapi's design system 23 | 24 | -------------------------------------------------------------------------------- /packages/docs/static/img/assets/icons/MapTrifold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/links/getting-started/ckeditor.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Ckeditor' 3 | displayed_sidebar: webtoolsLinksSidebar 4 | slug: /addons/links/ckeditor 5 | --- 6 | 7 | import ThemedImage from '@theme/ThemedImage'; 8 | import useBaseUrl from '@docusaurus/useBaseUrl'; 9 | 10 | # Ckeditor integration 11 | This addon has an integration with [the Ckeditor plugin](https://github.com/nshenderov/strapi-plugin-ckeditor). If the Ckeditor plugin is installed this addon will replace the link button in the editor. With this new button you can create internal links in the ckeditor. 12 | 13 | 20 | 21 | If you click the link button it will open a modal in which you can manage your links. 22 | 23 | 30 | -------------------------------------------------------------------------------- /packages/core/server/util/typeHelpers.ts: -------------------------------------------------------------------------------- 1 | type TupleEntry = 2 | T extends readonly [infer Head, ...infer Tail] ? 3 | TupleEntry : 4 | R; 5 | 6 | // eslint-disable-next-line @typescript-eslint/ban-types 7 | type ObjectEntry = 8 | // eslint-disable-next-line @typescript-eslint/ban-types 9 | T extends object ? 10 | { [K in keyof T]: [K, Required[K]] }[keyof T] extends infer E ? 11 | E extends [infer K, infer V] ? 12 | K extends string | number ? 13 | [`${K}`, V] : 14 | never : 15 | never : 16 | never : 17 | never; 18 | 19 | // eslint-disable-next-line @typescript-eslint/ban-types 20 | export type Entry = 21 | T extends readonly [unknown, ...unknown[]] ? 22 | TupleEntry : 23 | T extends ReadonlyArray ? 24 | [`${number}`, U] : 25 | ObjectEntry; 26 | 27 | // eslint-disable-next-line @typescript-eslint/ban-types 28 | export function typedEntries(object: T): ReadonlyArray> { 29 | return Object.entries(object) as unknown as ReadonlyArray>; 30 | } 31 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/routes/admin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | type: 'admin', 5 | routes: [ 6 | { 7 | method: 'GET', 8 | path: '/build-sitemap/:id', 9 | handler: 'core.buildSitemap', 10 | config: { 11 | policies: [], 12 | }, 13 | }, 14 | { 15 | method: 'GET', 16 | path: '/info/:id', 17 | handler: 'core.info', 18 | config: { 19 | policies: [], 20 | }, 21 | }, 22 | { 23 | method: 'POST', 24 | path: '/create-new-sitemap', 25 | handler: 'settings.createNewSitemap', 26 | config: { 27 | policies: [], 28 | }, 29 | }, 30 | { 31 | method: 'DELETE', 32 | path: '/:id', 33 | handler: 'settings.deleteSitemap', 34 | config: { 35 | policies: [], 36 | }, 37 | }, 38 | { 39 | method: 'GET', 40 | path: '/settings', 41 | handler: 'settings.getSettings', 42 | config: { 43 | policies: [], 44 | }, 45 | }, 46 | { 47 | method: 'PUT', 48 | path: '/settings/:id', 49 | handler: 'settings.updateSettings', 50 | config: { 51 | policies: [], 52 | }, 53 | }, 54 | ], 55 | }; 56 | -------------------------------------------------------------------------------- /packages/docs/docs/addons/redirects/getting-started/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'Introduction' 3 | displayed_sidebar: webtoolsRedirectsSidebar 4 | slug: /addons/redirects 5 | --- 6 | 7 | # Webtools Redirects addon 8 | 9 | This plugin offers the ability to configure **server-side redirects**. It integrates with Strapi Webtools to automatically generate redirects whenever the URL alias of a page changes. 10 | 11 | :::note 12 | This plugin acts as an extension of the core `strapi-plugin-webtools`. Please install and configure that before proceeding. 13 | ::: 14 | 15 | ## ✨ Features 16 | 17 | - **Automatic creation** - Automatic redirect creation when your URL changes ([learn more](/addons/redirects/configuration/auto-generate)) 18 | - **Chain detection** - Prevents you from creating redirects that create chains ([what are chains?](/addons/redirects/usage#what-is-a-redirect-chain)) 19 | - **Loop detection** - Prevents you from creating redirects that create loops ([what are loops?](/addons/redirects/usage#what-is-a-redirect-loop)) 20 | - **Custom redirects** - Create custom redirects, separate from Webtools pages 21 | - **API endpoint** - Fetch and use the redirects in your front-end ([REST API docs](/addons/redirects/api/rest)) 22 | 23 | -------------------------------------------------------------------------------- /packages/docs/src/scss/typography.scss: -------------------------------------------------------------------------------- 1 | /** General: Typography */ 2 | 3 | :root { 4 | --custom-heading-decorative-line-color: var(--strapi-neutral-150); 5 | } 6 | 7 | h1, h2, h3, h4, h5, h6 { 8 | --ifm-heading-color: var(--strapi-neutral-900); 9 | --ifm-heading-font-weight: 600; 10 | --ifm-code-font-size: 70%; 11 | } 12 | 13 | h1, .markdown h1:first-child { 14 | --ifm-h1-font-size: 35px; 15 | // --ifm-heading-line-height: 24px; // not good 16 | } 17 | 18 | h2, .markdown > h2 { 19 | --ifm-h2-font-size: 26px; 20 | // --ifm-heading-line-height: 24px; // not good 21 | } 22 | 23 | h3, .markdown > h3 { 24 | --ifm-h3-font-size: var(--strapi-font-size-lg); 25 | @include flex-row; 26 | } 27 | 28 | h2 { 29 | position: relative; 30 | 31 | &:after { 32 | content: " "; 33 | position: absolute; 34 | left: 0; 35 | right: 0; 36 | bottom: -10px; 37 | height: 1px; 38 | background-color: var(--custom-heading-decorative-line-color); 39 | } 40 | } 41 | 42 | p, ul { 43 | img { 44 | display: inline-block; 45 | vertical-align: text-bottom; 46 | } 47 | } 48 | 49 | /** Dark mode */ 50 | @include dark { 51 | h1, h2, h3, h4, h5, h6 { 52 | --ifm-heading-color: var(--strapi-neutral-900); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground-5", 3 | "version": "0.1.0", 4 | "private": true, 5 | "description": "A Strapi application", 6 | "scripts": { 7 | "build": "strapi build", 8 | "deploy": "strapi deploy", 9 | "develop": "strapi develop", 10 | "start": "strapi start", 11 | "strapi": "strapi", 12 | "sitemap": "strapi-sitemap" 13 | }, 14 | "dependencies": { 15 | "@strapi/plugin-cloud": "^5.31.2", 16 | "@strapi/plugin-users-permissions": "^5.31.2", 17 | "@strapi/strapi": "^5.31.2", 18 | "better-sqlite3": "11.3.0", 19 | "react": "^18.0.0", 20 | "react-dom": "^18.0.0", 21 | "react-router-dom": "^6.0.0", 22 | "strapi-plugin-webtools": "link:.yalc/strapi-plugin-webtools", 23 | "styled-components": "^6.0.0", 24 | "webtools-addon-sitemap": "link:.yalc/webtools-addon-sitemap" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^20", 28 | "@types/pg": "^8", 29 | "@types/react": "^18", 30 | "@types/react-dom": "^18", 31 | "pg": "^8.13.1", 32 | "typescript": "^5" 33 | }, 34 | "engines": { 35 | "node": ">=18.0.0 <=22.x.x", 36 | "npm": ">=6.0.0" 37 | }, 38 | "strapi": { 39 | "uuid": "90b22b41-262b-40ae-9b68-3a3e507df1b9" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/server/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import { Core } from '@strapi/strapi'; 2 | 3 | export default ({ strapi }: { strapi: Core.Strapi }) => { 4 | try { 5 | // Register permission actions. 6 | const actions = [ 7 | { 8 | section: 'plugins', 9 | displayName: 'Access the overview page', 10 | uid: 'settings.overview', 11 | pluginName: 'webtools', 12 | }, 13 | { 14 | section: 'plugins', 15 | displayName: 'Access the URL alias list', 16 | uid: 'settings.list', 17 | pluginName: 'webtools', 18 | }, 19 | { 20 | section: 'plugins', 21 | displayName: 'Access the URL alias patterns', 22 | uid: 'settings.patterns', 23 | pluginName: 'webtools', 24 | }, 25 | { 26 | section: 'plugins', 27 | displayName: 'Access the URL alias sidebar', 28 | uid: 'edit-view.sidebar', 29 | pluginName: 'webtools', 30 | }, 31 | ]; 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 34 | (strapi.admin.services.permission.actionProvider.registerMany as (a: any) => void)(actions); 35 | } catch (error) { 36 | strapi.log.error(`Bootstrap failed. ${String(error)}`); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /packages/addons/sitemap/server/middlewares/auto-generate.js: -------------------------------------------------------------------------------- 1 | import { getPluginService } from '../utils/getPluginService'; 2 | 3 | // eslint-disable-next-line max-len 4 | const autoGenerateMiddleware = async (context, next) => { 5 | const { uid, action } = context; 6 | 7 | // Only add the middleware if auto-generate is enabled. 8 | if (!strapi.config.get('plugin::webtools-addon-sitemap.autoGenerate')) { 9 | return next(); 10 | } 11 | 12 | const settings = await getPluginService('settings').getConfig(); 13 | const defaultSitemapSettings = settings?.sitemaps?.default; 14 | 15 | // Only add the middleware if the content type is added to the sitemap. 16 | if (!defaultSitemapSettings?.contentTypes || !Object.keys(defaultSitemapSettings?.contentTypes).includes(uid)) { 17 | return next(); 18 | } 19 | 20 | // Only add the middleware for the create, update and delete action. 21 | if (!['create', 'update', 'delete'].includes(action)) { 22 | return next(); 23 | } 24 | 25 | // Perform the action. 26 | const document = await next(); 27 | 28 | // Generate the sitemap. 29 | getPluginService('core').createSitemap('default'); 30 | 31 | // Return the document 32 | return document; 33 | }; 34 | 35 | export default autoGenerateMiddleware; 36 | --------------------------------------------------------------------------------