mirror of
https://github.com/ComfortablyCoding/strapi-plugin-slugify.git
synced 2025-06-23 00:00:35 -04:00
Compare commits
No commits in common. "master" and "2.3.5" have entirely different histories.
4
.github/workflows/npm-publish.yml
vendored
4
.github/workflows/npm-publish.yml
vendored
@ -13,10 +13,10 @@ jobs:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node v18
|
||||
- name: Install Node v14
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '18.x'
|
||||
node-version: '14.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Yarn
|
||||
|
@ -54,9 +54,7 @@ module.exports = ({ env }) => ({
|
||||
|
||||
This will listen for any record created or updated in the article content type and set a slugified value for the slug field automatically based on the title field.
|
||||
|
||||
> Note: To rewrite the same field (e.g. `title` is both a reference and a slug) use `title` as the `field` and `references` value.
|
||||
|
||||
> Note: Compound slugs (basing the slug on multiple fields) can be achieved by passing an array of fields to the `references` property (e.g. `references: ['date','title']`).
|
||||
> Note that if you want to rewrite the same field (so `title` is both a reference and a slug) then you just put `title` for both the `field` and `references` properties.
|
||||
|
||||
**IMPORTANT NOTE**: Make sure any sensitive data is stored in env files.
|
||||
|
||||
|
20
package.json
20
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package",
|
||||
"name": "strapi-plugin-slugify",
|
||||
"version": "2.3.8",
|
||||
"version": "2.3.5",
|
||||
"description": "A plugin for Strapi Headless CMS that provides the ability to auto slugify a field for any content type.",
|
||||
"scripts": {
|
||||
"lint": "eslint . --fix",
|
||||
@ -26,18 +26,20 @@
|
||||
"url": "https://github.com/ComfortablyCoding/strapi-plugin-slugify/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sindresorhus/slugify": "1.1.0",
|
||||
"@sindresorhus/slugify": "1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"prettier": "^3.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@strapi/strapi": "^4.14.0",
|
||||
"@strapi/utils": "^4.14.0",
|
||||
"lodash": "^4.17.21",
|
||||
"yup": "^0.32.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"prettier": "^3.1.0"
|
||||
},
|
||||
"strapi": {
|
||||
"displayName": "Slugify",
|
||||
"name": "slugify",
|
||||
@ -45,7 +47,7 @@
|
||||
"kind": "plugin"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <=20.x.x",
|
||||
"node": ">=14.19.1 <=18.x.x",
|
||||
"npm": ">=6.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
|
@ -1,11 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const { NotFoundError } = require('@strapi/utils').errors;
|
||||
const { getPluginService } = require('../utils/getPluginService');
|
||||
const { transformResponse } = require('@strapi/strapi/dist/core-api/controller/transform');
|
||||
const { isValidFindSlugParams } = require('../utils/isValidFindSlugParams');
|
||||
const { sanitizeOutput } = require('../utils/sanitizeOutput');
|
||||
const { hasRequiredModelScopes } = require('../utils/hasRequiredModelScopes');
|
||||
const transform = require('../utils/transform');
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
async findSlug(ctx) {
|
||||
@ -13,23 +14,15 @@ module.exports = ({ strapi }) => ({
|
||||
const { modelName, slug } = ctx.request.params;
|
||||
const { auth } = ctx.state;
|
||||
|
||||
try {
|
||||
isValidFindSlugParams({
|
||||
modelName,
|
||||
slug,
|
||||
modelsByName,
|
||||
});
|
||||
} catch (error) {
|
||||
return ctx.badRequest(error.message);
|
||||
}
|
||||
isValidFindSlugParams({
|
||||
modelName,
|
||||
slug,
|
||||
modelsByName,
|
||||
});
|
||||
|
||||
const { uid, field, contentType } = modelsByName[modelName];
|
||||
|
||||
try {
|
||||
await hasRequiredModelScopes(strapi, uid, auth);
|
||||
} catch (error) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
await hasRequiredModelScopes(strapi, uid, auth);
|
||||
|
||||
// add slug filter to any already existing query restrictions
|
||||
let query = ctx.query || {};
|
||||
@ -47,9 +40,9 @@ module.exports = ({ strapi }) => ({
|
||||
|
||||
if (data) {
|
||||
const sanitizedEntity = await sanitizeOutput(data, contentType, auth);
|
||||
ctx.body = transform.response({ data: sanitizedEntity, schema: contentType });
|
||||
ctx.body = transformResponse(sanitizedEntity, {}, { contentType });
|
||||
} else {
|
||||
ctx.notFound();
|
||||
throw new NotFoundError();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -3,7 +3,6 @@ const { getPluginService } = require('../utils/getPluginService');
|
||||
const { isValidFindSlugParams } = require('../utils/isValidFindSlugParams');
|
||||
const { hasRequiredModelScopes } = require('../utils/hasRequiredModelScopes');
|
||||
const { sanitizeOutput } = require('../utils/sanitizeOutput');
|
||||
const { ForbiddenError, ValidationError } = require('@strapi/utils').errors;
|
||||
|
||||
const getCustomTypes = (strapi, nexus) => {
|
||||
const { naming } = getPluginService('utils', 'graphql');
|
||||
@ -55,23 +54,16 @@ const getCustomTypes = (strapi, nexus) => {
|
||||
const { modelName, slug, publicationState } = args;
|
||||
const { auth } = ctx.state;
|
||||
|
||||
try {
|
||||
isValidFindSlugParams({
|
||||
modelName,
|
||||
slug,
|
||||
modelsByName,
|
||||
publicationState,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new ValidationError(error.message);
|
||||
}
|
||||
isValidFindSlugParams({
|
||||
modelName,
|
||||
slug,
|
||||
modelsByName,
|
||||
publicationState,
|
||||
});
|
||||
|
||||
const { uid, field, contentType } = modelsByName[modelName];
|
||||
|
||||
try {
|
||||
await hasRequiredModelScopes(strapi, uid, auth);
|
||||
} catch (error) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
await hasRequiredModelScopes(strapi, uid, auth);
|
||||
|
||||
// build query
|
||||
let query = {
|
||||
|
@ -1,5 +1,12 @@
|
||||
const hasRequiredModelScopes = (strapi, uid, auth) =>
|
||||
strapi.auth.verify(auth, { scope: `${uid}.find` });
|
||||
const { ForbiddenError } = require('@strapi/utils').errors;
|
||||
|
||||
const hasRequiredModelScopes = async (strapi, uid, auth) => {
|
||||
try {
|
||||
await strapi.auth.verify(auth, { scope: `${uid}.find` });
|
||||
} catch (e) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
hasRequiredModelScopes,
|
||||
|
@ -1,97 +0,0 @@
|
||||
'use strict';
|
||||
const { isNil, isPlainObject } = require('lodash/fp');
|
||||
|
||||
function response({ data, schema }) {
|
||||
return transformResponse(data, {}, { contentType: schema });
|
||||
}
|
||||
|
||||
// adapted from https://github.com/strapi/strapi/blob/main/packages/core/strapi/src/core-api/controller/transform.ts
|
||||
function isEntry(property) {
|
||||
return property === null || isPlainObject(property) || Array.isArray(property);
|
||||
}
|
||||
|
||||
function isDZEntries(property) {
|
||||
return Array.isArray(property);
|
||||
}
|
||||
|
||||
function transformResponse(resource, meta = {}, opts = {}) {
|
||||
if (isNil(resource)) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
return {
|
||||
data: transformEntry(resource, opts?.contentType),
|
||||
meta,
|
||||
};
|
||||
}
|
||||
|
||||
function transformComponent(data, component) {
|
||||
if (Array.isArray(data)) {
|
||||
return data.map((datum) => transformComponent(datum, component));
|
||||
}
|
||||
|
||||
const res = transformEntry(data, component);
|
||||
|
||||
if (isNil(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const { id, attributes } = res;
|
||||
return { id, ...attributes };
|
||||
}
|
||||
|
||||
function transformEntry(entry, type) {
|
||||
if (isNil(entry)) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (Array.isArray(entry)) {
|
||||
return entry.map((singleEntry) => transformEntry(singleEntry, type));
|
||||
}
|
||||
|
||||
if (!isPlainObject(entry)) {
|
||||
throw new Error('Entry must be an object');
|
||||
}
|
||||
|
||||
const { id, ...properties } = entry;
|
||||
|
||||
const attributeValues = {};
|
||||
|
||||
for (const key of Object.keys(properties)) {
|
||||
const property = properties[key];
|
||||
const attribute = type && type.attributes[key];
|
||||
|
||||
if (attribute && attribute.type === 'relation' && isEntry(property) && 'target' in attribute) {
|
||||
const data = transformEntry(property, strapi.contentType(attribute.target));
|
||||
|
||||
attributeValues[key] = { data };
|
||||
} else if (attribute && attribute.type === 'component' && isEntry(property)) {
|
||||
attributeValues[key] = transformComponent(property, strapi.components[attribute.component]);
|
||||
} else if (attribute && attribute.type === 'dynamiczone' && isDZEntries(property)) {
|
||||
if (isNil(property)) {
|
||||
attributeValues[key] = property;
|
||||
}
|
||||
|
||||
attributeValues[key] = property.map((subProperty) => {
|
||||
return transformComponent(subProperty, strapi.components[subProperty.__component]);
|
||||
});
|
||||
} else if (attribute && attribute.type === 'media' && isEntry(property)) {
|
||||
const data = transformEntry(property, strapi.contentType('plugin::upload.file'));
|
||||
|
||||
attributeValues[key] = { data };
|
||||
} else {
|
||||
attributeValues[key] = property;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
attributes: attributeValues,
|
||||
// NOTE: not necessary for now
|
||||
// meta: {},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
response,
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user