Compare commits

...

2 Commits

Author SHA1 Message Date
daedalus
2d53829e94
fix(slugifyWithCount): persist slug count (#82)
* fix(slugification): persist slug count

* fix(slugification): ensure correct count used for old plugin versions and projects with existing slugs

* fix(slug CT): do not show plugin CT in content manager

* fix(syncSlugCount): ensure we have slugs to sync for createMany

* refactor(buildSlug): use early return
2022-12-07 21:19:12 -05:00
daedalus
9c336ebb1d
fix(engines): incorrect engine range (#87)
Resolves #86
2022-12-07 20:59:04 -05:00
9 changed files with 168 additions and 27 deletions

View File

@ -47,7 +47,7 @@
"kind": "plugin" "kind": "plugin"
}, },
"engines": { "engines": {
"node": ">=12.x.x <=16.x.x", "node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0" "npm": ">=6.0.0"
}, },
"keywords": [ "keywords": [

View File

@ -2,8 +2,15 @@
const { buildSettings } = require('./buildSettings'); const { buildSettings } = require('./buildSettings');
const { setupLifecycles } = require('./setupLifecycles'); const { setupLifecycles } = require('./setupLifecycles');
const { syncSlugCount } = require('./syncSlugCount');
module.exports = async () => {
const settings = await buildSettings();
if (settings.slugifyWithCount) {
// Ensure correct count used for old plugin versions and projects with existing slugs.
await syncSlugCount(settings);
}
module.exports = async ({ strapi }) => {
const settings = await buildSettings(strapi);
setupLifecycles(settings); setupLifecycles(settings);
}; };

View File

@ -0,0 +1,75 @@
'use strict';
const syncSlugCount = async (settings) => {
const entries = await strapi.entityService.findMany('plugin::slugify.slug', {
filters: { createdAt: { $gt: 1 } },
});
// if entries aready present we can skip sync
if (entries && entries.length) {
return;
}
strapi.log.info('[slugify] syncing slug count for registered content types');
const slugs = new Map();
// chec slugs in each reigistered model
for (const uid in settings.modelsByUID) {
if (!Object.hasOwnProperty.call(settings.modelsByUID, uid)) {
continue;
}
const model = settings.modelsByUID[uid];
// using db query to avoid the need to check if CT has draftAndPublish enabled
const modelEntries = await strapi.db.query(model.uid).findMany({
filters: { createdAt: { $gt: 1 } },
});
strapi.log.info(`[slugify] syncing slug count for ${model.uid}`);
for (const entry of modelEntries) {
const slug = entry[model.field];
if (!slug) {
continue;
}
const record = slugs.get(getNonAppendedSlug(slug));
if (!record) {
slugs.set(slug, { slug, count: 1 });
continue;
}
slugs.set(record.slug, { slug: record.slug, count: record.count + 1 });
}
strapi.log.info(`[slugify] sync for ${model.uid} completed`);
}
if (slugs.size) {
// create all required records
const createResponse = await strapi.db.query('plugin::slugify.slug').createMany({
data: [...slugs.values()],
});
strapi.log.info(
`[slugify] ${createResponse.count} out of ${slugs.size} slugs synced successfully`
);
} else {
strapi.log.info('[slugify] No syncable slugs found');
}
};
// removes any appended number from a slug/string if found
const getNonAppendedSlug = (slug) => {
const match = slug.match('[\\-]{1}[\\d]+$');
if (!match) {
return slug;
}
return slug.replace(match[0], '');
};
module.exports = {
syncSlugCount,
};

View File

@ -0,0 +1,9 @@
'use strict';
const slugSchema = require('./slug/schema.json');
module.exports = {
slug: {
schema: slugSchema,
},
};

View File

@ -0,0 +1,29 @@
{
"kind": "collectionType",
"collectionName": "slugs",
"info": {
"singularName": "slug",
"pluralName": "slugs",
"displayName": "slug"
},
"options": {
"draftAndPublish": false,
"comment": ""
},
"pluginOptions": {
"content-manager": {
"visible": false
},
"content-type-builder": {
"visible": false
}
},
"attributes": {
"slug": {
"type": "text"
},
"count": {
"type": "integer"
}
}
}

View File

@ -2,6 +2,7 @@
const bootstrap = require('./bootstrap'); const bootstrap = require('./bootstrap');
const config = require('./config'); const config = require('./config');
const contentTypes = require('./content-types');
const controllers = require('./controllers'); const controllers = require('./controllers');
const register = require('./register'); const register = require('./register');
const routes = require('./routes'); const routes = require('./routes');
@ -10,6 +11,7 @@ const services = require('./services');
module.exports = { module.exports = {
bootstrap, bootstrap,
config, config,
contentTypes,
controllers, controllers,
register, register,
routes, routes,

View File

@ -0,0 +1,40 @@
const slugify = require('@sindresorhus/slugify');
const buildSlug = async (string, settings) => {
let slug = slugify(string, settings.slugifyOptions);
// slugify with count
if (!settings.slugifyWithCount) {
return slug;
}
const slugEntry = await strapi.db.query('plugin::slugify.slug').findOne({
select: ['id', 'count'],
where: { slug },
});
// if no result then count is 1 and base slug is returned
if (!slugEntry) {
await strapi.entityService.create('plugin::slugify.slug', {
data: {
slug,
count: 1,
},
});
return slug;
}
const count = slugEntry.count + 1;
await strapi.entityService.update('plugin::slugify.slug', slugEntry.id, {
data: {
count,
},
});
return `${slug}-${count}`;
};
module.exports = {
buildSlug,
};

View File

@ -1,10 +1,10 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const { toSlug } = require('../../utils/slugification');
const { getPluginService } = require('../../utils/getPluginService'); const { getPluginService } = require('../../utils/getPluginService');
const { shouldUpdateSlug } = require('./shoudUpdateSlug'); const { shouldUpdateSlug } = require('./shoudUpdateSlug');
const { getReferenceFieldValues } = require('./getReferenceFieldValues'); const { getReferenceFieldValues } = require('./getReferenceFieldValues');
const { buildSlug } = require('./buildSlug');
module.exports = ({ strapi }) => ({ module.exports = ({ strapi }) => ({
async slugify(ctx) { async slugify(ctx) {
@ -40,13 +40,10 @@ module.exports = ({ strapi }) => ({
} }
referenceFieldValues = referenceFieldValues.join(' '); referenceFieldValues = referenceFieldValues.join(' ');
const toSlugOptions = settings.slugifyOptions;
if (settings.slugifyWithCount) {
toSlugOptions.slugifyWithCount = settings.slugifyWithCount;
}
// update slug field based on action type // update slug field based on action type
const slug = toSlug(referenceFieldValues, toSlugOptions); const slug = await buildSlug(referenceFieldValues, settings);
if (ctx.action === 'beforeCreate' || ctx.action === 'beforeUpdate') { if (ctx.action === 'beforeCreate' || ctx.action === 'beforeUpdate') {
data[field] = slug; data[field] = slug;
} else if (ctx.action === 'afterCreate') { } else if (ctx.action === 'afterCreate') {

View File

@ -1,18 +0,0 @@
'use strict';
const _ = require('lodash');
const slugify = require('@sindresorhus/slugify');
const slugifyWithCount = slugify.counter();
const toSlug = (string, options) => {
if (options.slugifyWithCount) {
_.omit(options, 'slugifyWithCount');
return slugifyWithCount(string, options);
}
return slugify(string, options);
};
module.exports = {
toSlug,
};