Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
strapi-plugin-permalinks
Advanced tools
A plugin for Strapi CMS to enable a URL path field for content types with nested relationships.
A plugin for Strapi CMS to enable permalinks for content types with nested relationships.
i18n
Strapi plugin.yarn add strapi-plugin-permalinks@latest
To get started, let's create a simple Page
collection that uses a permalink field.
Use the "Custom" tab in the content type builder to add the permalink field to the model.
After adding a permalink field through the content type builder, there are additional targetField
and targetRelation
props that will need to be manually added to the permalink schema attribute.
NOTE: Strapi does not currently provide the necessary params to dynamically render a list containing the other field names as select menu options in the content type builder, which is why
targetField
andtargetRelation
need to be added manually for now. This will be updated in the future.
Page
// src/api/page/content-types/page/schema.json
{
"kind": "collectionType",
"collectionName": "pages",
"info": {
"singularName": "page",
"pluralName": "pages",
"displayName": "Page"
},
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"title": {
"type": "string",
"required": true
},
"slug": {
"type": "customField",
"customField": "plugin::permalinks.permalink",
"required": true,
"pluginOptions": {
"permalinks": {
"targetField": "title",
"targetRelation": "parent"
}
}
},
"content": {
"type": "richtext"
},
"parent": {
"type": "relation",
"relation": "oneToOne",
"target": "api::page.page"
}
}
}
targetField
This is the same targetField
prop used with uid
field types. It should point to a string type field which will be used to make suggestions for the unique permalink value.
targetRelation
This prop should point to a oneToOne
relation field which will be used as it's "parent" relation.
In addition to the permalink attribute itself, we also need to enable the plugin config.
property | type (default) | description |
---|---|---|
contentTypes | array ([] ) | An array of objects describing related UIDs that use permalink fields. |
contentTypes[].uids | array ([] ) | The UID values for related collection types. |
contentTypes[].url | string | The URL string template for a UID which can be used to create an absolute URL. See section about mapping data into the URLs for greater customization. |
contentTypes[].copy | boolean (true ) | Set to false to disable the "Copy permalink" button that appears in the edit view sidebar. |
lowercase | boolean (true ) | If set to true , it will ensure the input value is always lowercased. |
contentTypes
An array of objects describing related UIDs that use permalink fields.
Let's add our Page
content type to the plugin config, which will enable it in middlewares, lifecycles, etc. and also help keep related collections synced as data changes.
// config/plugins.js
'use strict';
module.exports = {
permalinks: {
config: {
contentTypes: [
{
uids: ['api::page.page'],
},
],
},
},
};
In addition to our generic Page
collection, let's say we have other collections representing different types of pages, such as HelpPages
and ProductPages
.
We will want to keep them all in sync with unique permalinks. Our config might look like the code below:
// config/plugins.js
'use strict';
module.exports = {
permalinks: {
config: {
contentTypes: [
{
uids: [
'api::page.page',
'api::product-page.product-page',
],
},
{
uids: ['api::help-page.help-page'],
},
],
},
},
};
This informs the plugin which collections should avoid permalink conflicts with other collections. Order of uids
does not matter here.
Here is a recap of what is achieved in this example:
Pages
have parent Pages
and will sync across Pages
and ProductPages
.ProductPages
have parent Pages
and will sync across Pages
and ProductPages
.HelpPages
have parent HelpPages
and will sync across HelpPages
.A permalink input is almost identical to a regular uid
input in Strapi and therefore will not store a full URL string in the database. Instead we only save the unique path part of the URL.
In order to use the full permalink URL for the "Copy permalink" feature or to use the full permalink in API responses, you must configure the url
option.
// config/plugins.js
'use strict';
module.exports = ({ env }) => ({
permalinks: {
config: {
contentTypes: [
{
uids: [
'api::page.page',
'api::product-page.product-page',
],
url: `${env('STRAPI_PERMALINKS_BASE_URL')}/{slug}`,
},
{
uids: ['api::help-page.help-page'],
url: `${env('STRAPI_PERMALINKS_BASE_URL')}/help/{slug}`,
},
],
},
},
});
With i18n
enabled for the slug
field, you can insert the locale
into the permalink value using the url
config option.
Currently this does not keep the locale with the value in the database. In a future release, a change may be introduced where the locale value is optionally saved with the slug value in the database.
// config/plugins.js
'use strict';
module.exports = ({ env }) => ({
permalinks: {
config: {
contentTypes: [
// Include `locale` in the localized slug.
{
uids: [
'api::page.page',
'api::product-page.product-page',
],
url: '{locale}/{slug}',
},
// Example with localized absolute URL and env vars.
{
uids: ['api::help-page.help-page'],
url: `${env('STRAPI_PERMALINKS_BASE_URL')}/{locale}/help/{slug}`,
},
// Another example with a different locale position.
{
uids: ['api::example.example'],
url: 'https://{locale}.example.com/{slug}',
},
],
},
},
});
Notice how the
HelpPage
example has/help/
prepending it's slug value, which already makes it unique againstPage
andProductPage
.
By using {curly_braces}
, you can map values from the entry data into your permalink URLs to customize the URL however you like. Unmatched values will be replaced with an empty string.
Relation fields are not currently supported but that will change in a future update which will allow the
url
option to use something like{category.slug}
to inject a category slug into the path.
{
uids: ['api::product-page.product-page'],
url: `${env('STRAPI_PERMALINKS_BASE_URL')}/{locale}/{slug}`,
}
The "Copy permalink" button located in the edit view sidebar can be disabled with the copy: false
prop. This value is true
by default.
{
uids: ['api::page.page'],
url: `${env('STRAPI_PERMALINKS_BASE_URL')}/{slug}`,
copy: false,
}
lowercase
Defaults to true
. It will ensure the permalink value is always lowercased.
NOTE: If you are setting this option to
true
when it was previously set tofalse
, it will not automatically lowercase existing permalinks in the database. You will need to lowercase existing permalinks yourself, which can be easily done with a database migration script.
// config/plugins.js
'use strict';
module.exports = ({ env }) => ({
permalinks: {
config: {
lowercase: false,
contentTypes: [
// etc.
],
},
},
});
// database/migrations/100-lowercase-permalinks.js
'use strict';
module.exports = {
async up(knex) {
const rows = await knex('pages');
const promisedUpdates = rows.map(row => {
return knex(uid)
.where({ id: row.id })
.update({ slug: row.slug.toLowerCase() });
});
await Promise.all(promisedUpdates);
},
down() {},
};
Assign a parent relation to an entity to automatically generate a URL path that includes the slugs of it's parent entities.
All child entities will automatically have their permalink values updated when the permalink of their ancestor changes. This extends down to all descendants.
Deleting an entity that has children will orphan those children. Strapi will automatically remove the parent relation from the child entities but no other changes to their data will occur.
If orphaned pages exist, you will see their slug value in the content manager list view as a red label instead of plain text.
Editing the orphaned page will display a warning and an error message on the permalink field. From here you can assign a new parent or no parent at all. Upon saving, any children of the entity will also update their target fields to reflect to new parent permalinks.
Remember to rebuild your app after making changes to some config or other code.
yarn build
# OR
yarn develop
If you are enjoying this plugin and feel extra appreciative, you can buy me a beer or 3 🍺🍺🍺.
uid
field, then deprecate it in plugin config (not currently possible).FAQs
A plugin for Strapi CMS to enable a URL path field for content types with nested relationships.
We found that strapi-plugin-permalinks demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.