@mattisvensson/strapi-plugin-webatlas 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +2 -1
  2. package/dist/admin/FullLoader-Cmsf8xS6.js +1 -0
  3. package/dist/admin/FullLoader-Cmsf8xS6.js.map +1 -0
  4. package/dist/admin/FullLoader-CrPED_dY.mjs +1 -0
  5. package/dist/admin/FullLoader-CrPED_dY.mjs.map +1 -0
  6. package/dist/admin/{SettingTitle-DbsxB1V9.mjs → SettingTitle-B1IaU3qs.mjs} +2 -1
  7. package/dist/admin/SettingTitle-B1IaU3qs.mjs.map +1 -0
  8. package/dist/admin/{SettingTitle-uw1S5OmC.js → SettingTitle-BjE_2u6R.js} +2 -1
  9. package/dist/admin/SettingTitle-BjE_2u6R.js.map +1 -0
  10. package/dist/admin/de-B5pRvs13.mjs +1 -0
  11. package/dist/admin/de-B5pRvs13.mjs.map +1 -0
  12. package/dist/admin/de-CqU1FU8C.js +1 -0
  13. package/dist/admin/de-CqU1FU8C.js.map +1 -0
  14. package/dist/admin/en-BE-zzIv8.mjs +1 -0
  15. package/dist/admin/en-BE-zzIv8.mjs.map +1 -0
  16. package/dist/admin/en-C7I90FwV.js +1 -0
  17. package/dist/admin/en-C7I90FwV.js.map +1 -0
  18. package/dist/admin/{index-BXt-QjKo.js → index-AVI3QJ0R.js} +2 -1
  19. package/dist/admin/index-AVI3QJ0R.js.map +1 -0
  20. package/dist/admin/{index-CGsC8P9P.js → index-B90eSO4a.js} +3 -2
  21. package/dist/admin/index-B90eSO4a.js.map +1 -0
  22. package/dist/admin/{index-tPrfjOIn.mjs → index-BIEUXWe7.mjs} +2 -1
  23. package/dist/admin/index-BIEUXWe7.mjs.map +1 -0
  24. package/dist/admin/{index-CUaBX_v-.mjs → index-BbnlyBrZ.mjs} +3 -2
  25. package/dist/admin/index-BbnlyBrZ.mjs.map +1 -0
  26. package/dist/admin/{index-BKWY9Ta-.mjs → index-Cf9j0bn2.mjs} +2 -1
  27. package/dist/admin/index-Cf9j0bn2.mjs.map +1 -0
  28. package/dist/admin/{index-BYlmJycd.js → index-D8bG0YFB.js} +3 -2
  29. package/dist/admin/index-D8bG0YFB.js.map +1 -0
  30. package/dist/admin/{index-CIr8o1RP.mjs → index-DCYCtKrj.mjs} +118 -106
  31. package/dist/admin/index-DCYCtKrj.mjs.map +1 -0
  32. package/dist/admin/{index-DkqiqVx2.js → index-JLpXBQVL.js} +2 -1
  33. package/dist/admin/index-JLpXBQVL.js.map +1 -0
  34. package/dist/admin/{index-B07KlG03.mjs → index-Y_PYIiRA.mjs} +3 -2
  35. package/dist/admin/index-Y_PYIiRA.mjs.map +1 -0
  36. package/dist/admin/{index-DC5WwNdi.js → index-Yq-QR8t0.js} +118 -106
  37. package/dist/admin/index-Yq-QR8t0.js.map +1 -0
  38. package/dist/admin/index.js +2 -1
  39. package/dist/admin/index.js.map +1 -0
  40. package/dist/admin/index.mjs +2 -1
  41. package/dist/admin/index.mjs.map +1 -0
  42. package/dist/server/index.js +384 -259
  43. package/dist/server/index.js.map +1 -0
  44. package/dist/server/index.mjs +384 -259
  45. package/dist/server/index.mjs.map +1 -0
  46. package/package.json +1 -1
  47. package/dist/admin/src/index.d.ts +0 -12
  48. package/dist/server/src/index.d.ts +0 -301
@@ -1,15 +1,22 @@
1
- function transformToUrl(input) {
1
+ function transformToUrl(input, replaceSlash = true) {
2
2
  const specialCharMap = {
3
3
  "ü": "ue",
4
4
  "ä": "ae",
5
- "ö": "oe"
5
+ "ö": "oe",
6
+ "ß": "ss"
6
7
  };
7
8
  if (!input || typeof input !== "string") return "";
8
9
  input = input.toLowerCase();
9
10
  input = input.replace(/\/+/g, "/");
10
11
  input = input.startsWith("/") ? input.slice(1) : input;
11
12
  input = input.endsWith("/") ? input.slice(0, -1) : input;
12
- input = input.replace(/\//g, "-");
13
+ if (replaceSlash) {
14
+ input = input.replace(/\//g, "-");
15
+ } else {
16
+ input = input.replace(/\/+/g, "/");
17
+ input = input.startsWith("/") ? input.slice(1) : input;
18
+ input = input.endsWith("/") ? input.slice(0, -1) : input;
19
+ }
13
20
  for (const char in specialCharMap) {
14
21
  const regex = new RegExp(char, "g");
15
22
  input = input.replace(regex, specialCharMap[char]);
@@ -28,36 +35,28 @@ const PLUGIN_ID = pluginPkg.strapi.name.replace(/^(@[^-,.][\w,-]+\/|strapi-)plug
28
35
  const waNavigation = `plugin::${PLUGIN_ID}.navigation`;
29
36
  const waNavItem = `plugin::${PLUGIN_ID}.navitem`;
30
37
  const waRoute = `plugin::${PLUGIN_ID}.route`;
31
- async function checkPathExists(path, targetRoutePath) {
32
- const entities = await strapi.documents(waRoute).findMany({
33
- filters: {
34
- $or: [
35
- { path },
36
- { slug: path },
37
- { uidPath: path },
38
- { canonicalPath: path }
39
- ]
40
- }
41
- });
42
- if (targetRoutePath && entities && entities[0]?.path === targetRoutePath)
43
- return false;
44
- return entities?.length > 0;
38
+ async function checkPathExists(path, excludeDocumentId) {
39
+ const filters = {
40
+ $or: [
41
+ { path },
42
+ { uidPath: path },
43
+ { canonicalPath: path }
44
+ ]
45
+ };
46
+ if (excludeDocumentId) {
47
+ filters.documentId = { $ne: excludeDocumentId };
48
+ }
49
+ const entity = await strapi.documents(waRoute).findFirst({ filters });
50
+ return !!entity;
45
51
  }
46
52
  async function duplicateCheck(initialPath, targetRouteDocumentId) {
47
53
  try {
48
54
  let uniquePath = initialPath;
49
- let targetRoutePath = null;
50
55
  let counter = 1;
51
- if (targetRouteDocumentId) {
52
- const route2 = await strapi.documents(waRoute).findOne({
53
- documentId: targetRouteDocumentId
54
- });
55
- if (route2) targetRoutePath = route2.path;
56
- }
57
- let exists = await checkPathExists(uniquePath, targetRoutePath);
56
+ let exists = await checkPathExists(uniquePath, targetRouteDocumentId);
58
57
  while (exists) {
59
58
  uniquePath = `${initialPath}-${counter}`;
60
- exists = await checkPathExists(uniquePath);
59
+ exists = await checkPathExists(uniquePath, targetRouteDocumentId);
61
60
  counter++;
62
61
  }
63
62
  return uniquePath;
@@ -249,12 +248,14 @@ function extractRouteAndItems(items) {
249
248
  return items.map((item) => {
250
249
  const route2 = { ...item.route };
251
250
  if (!route2) return null;
252
- if (item.depth) {
253
- route2.depth = item.depth;
251
+ let depth = null;
252
+ if (item.depth !== void 0) {
253
+ depth = item.depth;
254
254
  }
255
+ let children = null;
255
256
  if (item.items?.length > 0) {
256
257
  const items2 = extractRouteAndItems(item.items);
257
- if (items2.length > 0) route2.items = items2;
258
+ if (items2.length > 0) children = items2;
258
259
  }
259
260
  delete route2.relatedContentType;
260
261
  delete route2.relatedDocumentId;
@@ -265,7 +266,9 @@ function extractRouteAndItems(items) {
265
266
  return {
266
267
  __component: route2.type === "wrapper" ? `${PLUGIN_ID}.wrapper` : `${PLUGIN_ID}.route`,
267
268
  type: route2.type,
268
- ...route2
269
+ ...route2,
270
+ depth: depth !== null ? depth : void 0,
271
+ items: children !== null ? children : void 0
269
272
  };
270
273
  });
271
274
  }
@@ -4316,6 +4319,7 @@ function removeWaFields(obj) {
4316
4319
  delete obj["webatlas_path"];
4317
4320
  delete obj["webatlas_override"];
4318
4321
  delete obj["webatlas_parent"];
4322
+ delete obj["webatlas"];
4319
4323
  return obj;
4320
4324
  }
4321
4325
  async function buildCanonicalPath(slug, parentDocumentId) {
@@ -4331,26 +4335,55 @@ async function buildCanonicalPath(slug, parentDocumentId) {
4331
4335
  return slug;
4332
4336
  }
4333
4337
  }
4334
- async function cascadeCanonicalPathUpdates(parentRouteId, newParentCanonicalPath) {
4335
- const children = await strapi.db.query(waRoute).findMany({
4336
- where: {
4337
- parent: {
4338
- documentId: parentRouteId
4338
+ async function cascadePathUpdates({
4339
+ validatedParentPath,
4340
+ parentRouteDocumentId,
4341
+ canonicalPath,
4342
+ isOverride
4343
+ }) {
4344
+ try {
4345
+ const children = await strapi.db.query(waRoute).findMany({
4346
+ where: {
4347
+ parent: {
4348
+ documentId: parentRouteDocumentId
4349
+ }
4339
4350
  }
4340
- }
4341
- });
4342
- for (const child of children) {
4343
- const newCanonicalPath = `${newParentCanonicalPath}/${child.slug}`;
4344
- const updateData = {
4345
- canonicalPath: newCanonicalPath,
4346
- // Only update path if not manually overridden
4347
- ...child.isOverride ? {} : { path: newCanonicalPath }
4348
- };
4349
- await strapi.documents(waRoute).update({
4350
- documentId: child.documentId,
4351
- data: updateData
4352
4351
  });
4353
- await cascadeCanonicalPathUpdates(child.documentId, newCanonicalPath);
4352
+ for (const child of children) {
4353
+ const newCanonicalPath = `${canonicalPath}/${child.slug}`;
4354
+ const newPath = isOverride ? `${validatedParentPath}/${child.slug}` : newCanonicalPath;
4355
+ const validatedCanonicalPath = await duplicateCheck(newCanonicalPath, child.documentId);
4356
+ const validatedPath = isOverride ? await duplicateCheck(newPath, child.documentId) : validatedCanonicalPath;
4357
+ await strapi.db.query(waRoute).updateMany({
4358
+ where: { documentId: child.documentId },
4359
+ data: {
4360
+ canonicalPath: validatedCanonicalPath,
4361
+ path: validatedPath
4362
+ }
4363
+ });
4364
+ const existingEntry = await strapi.db.query(child.relatedContentType).findOne({
4365
+ where: { documentId: child.relatedDocumentId }
4366
+ });
4367
+ if (existingEntry) {
4368
+ await strapi.db.query(child.relatedContentType).updateMany({
4369
+ where: { documentId: child.relatedDocumentId },
4370
+ data: {
4371
+ webatlas: {
4372
+ ...existingEntry.webatlas,
4373
+ path: validatedPath
4374
+ }
4375
+ }
4376
+ });
4377
+ }
4378
+ await cascadePathUpdates({
4379
+ validatedParentPath: validatedPath,
4380
+ parentRouteDocumentId: child.documentId,
4381
+ canonicalPath: validatedCanonicalPath,
4382
+ isOverride
4383
+ });
4384
+ }
4385
+ } catch (err) {
4386
+ strapi.log.error(err);
4354
4387
  }
4355
4388
  }
4356
4389
  async function getRouteDescendants(routeId) {
@@ -4677,8 +4710,66 @@ const migration_001_canonical_path = {
4677
4710
  }
4678
4711
  }
4679
4712
  };
4713
+ const migration_002_webatlas_json_field = {
4714
+ version: "002",
4715
+ description: "Migrate webatlas_path, webatlas_parent and webatlas_override text fields into the webatlas JSON field",
4716
+ async up(strapi2) {
4717
+ const knex = strapi2.db.connection;
4718
+ const enabledContentTypes = Object.values(strapi2.contentTypes).filter(
4719
+ (ct) => ct.pluginOptions?.webatlas?.enabled === true
4720
+ );
4721
+ for (const contentType of enabledContentTypes) {
4722
+ const tableName = strapi2.db.metadata.get(contentType.uid)?.tableName ?? contentType.collectionName;
4723
+ strapi2.log.info(`[webatlas] Processing table: ${tableName} (${contentType.uid})`);
4724
+ const [hasPathCol, hasOverrideCol, hasParentCol, hasWebatlasCol] = await Promise.all([
4725
+ knex.schema.hasColumn(tableName, "webatlas_path"),
4726
+ knex.schema.hasColumn(tableName, "webatlas_override"),
4727
+ knex.schema.hasColumn(tableName, "webatlas_parent"),
4728
+ knex.schema.hasColumn(tableName, "webatlas")
4729
+ ]);
4730
+ const hasAnyOldCol = hasPathCol || hasOverrideCol || hasParentCol;
4731
+ if (!hasAnyOldCol) {
4732
+ strapi2.log.info(`[webatlas] No old fields found in ${tableName}, skipping data migration`);
4733
+ } else if (!hasWebatlasCol) {
4734
+ strapi2.log.warn(`[webatlas] New "webatlas" column not found in ${tableName} — schema may not have synced yet. Skipping data migration, old columns will still be dropped.`);
4735
+ } else {
4736
+ const selectCols = ["id"];
4737
+ if (hasPathCol) selectCols.push("webatlas_path");
4738
+ if (hasOverrideCol) selectCols.push("webatlas_override");
4739
+ if (hasParentCol) selectCols.push("webatlas_parent");
4740
+ const rows = await knex(tableName).select(selectCols);
4741
+ let migratedCount = 0;
4742
+ for (const row of rows) {
4743
+ const path = row.webatlas_path || "";
4744
+ const isOverride = Boolean(row.webatlas_override);
4745
+ const parentDocumentId = row.webatlas_parent || null;
4746
+ if (!path && !isOverride && !parentDocumentId) continue;
4747
+ const slug = path ? path.split("/").pop() || "" : "";
4748
+ await knex(tableName).where({ id: row.id }).update({
4749
+ webatlas: JSON.stringify({ path, slug, isOverride, parentDocumentId })
4750
+ });
4751
+ migratedCount++;
4752
+ }
4753
+ strapi2.log.info(`[webatlas] Migrated ${migratedCount} / ${rows.length} rows in ${tableName}`);
4754
+ }
4755
+ const colsToDrop = [
4756
+ hasPathCol && "webatlas_path",
4757
+ hasOverrideCol && "webatlas_override",
4758
+ hasParentCol && "webatlas_parent"
4759
+ ].filter(Boolean);
4760
+ if (colsToDrop.length > 0) {
4761
+ await knex.schema.table(tableName, (table) => {
4762
+ colsToDrop.forEach((col) => table.dropColumn(col));
4763
+ });
4764
+ strapi2.log.info(`[webatlas] Dropped columns [${colsToDrop.join(", ")}] from ${tableName}`);
4765
+ }
4766
+ }
4767
+ strapi2.log.info("[webatlas] webatlas JSON field migration completed");
4768
+ }
4769
+ };
4680
4770
  const migrations = [
4681
- migration_001_canonical_path
4771
+ migration_001_canonical_path,
4772
+ migration_002_webatlas_json_field
4682
4773
  ];
4683
4774
  const runMigrations = async (strapi2) => {
4684
4775
  const pluginStore = strapi2.store({ type: "plugin", name: PLUGIN_ID });
@@ -4712,54 +4803,47 @@ const runMigrations = async (strapi2) => {
4712
4803
  }
4713
4804
  strapi2.log.info("[webatlas] All migrations completed successfully");
4714
4805
  };
4715
- const bootstrap = async ({ strapi: strapi2 }) => {
4716
- try {
4717
- await runMigrations(strapi2);
4718
- const actions = [
4719
- {
4720
- section: "plugins",
4721
- displayName: "Navigation page",
4722
- uid: "page.navigation",
4723
- subCategory: "Pages",
4724
- pluginName: PLUGIN_ID
4725
- },
4726
- {
4727
- section: "plugins",
4728
- displayName: "Routes page",
4729
- uid: "page.routes",
4730
- subCategory: "Pages",
4731
- pluginName: PLUGIN_ID
4732
- },
4733
- {
4734
- section: "plugins",
4735
- displayName: "General page",
4736
- uid: "settings.general",
4737
- subCategory: "Settings",
4738
- pluginName: PLUGIN_ID
4739
- },
4740
- {
4741
- section: "plugins",
4742
- displayName: "Navigation page",
4743
- uid: "settings.navigation",
4744
- subCategory: "Settings",
4745
- pluginName: PLUGIN_ID
4746
- },
4747
- {
4748
- section: "plugins",
4749
- displayName: "Aside panel",
4750
- uid: "cm.aside",
4751
- subCategory: "Content Manager",
4752
- pluginName: PLUGIN_ID
4753
- }
4754
- ];
4755
- strapi2.admin.services.permission.actionProvider.registerMany(actions);
4756
- } catch (error) {
4757
- strapi2.log.error(`Bootstrap failed. ${String(error)}`);
4758
- }
4759
- const contentTypes2 = strapi2.contentTypes;
4760
- const enabledContentTypes = Object.values(contentTypes2).filter(
4761
- (type) => type.pluginOptions?.webatlas?.enabled === true
4762
- );
4806
+ function registerPermissions(strapi2) {
4807
+ const actions = [
4808
+ {
4809
+ section: "plugins",
4810
+ displayName: "Navigation page",
4811
+ uid: "page.navigation",
4812
+ subCategory: "Pages",
4813
+ pluginName: PLUGIN_ID
4814
+ },
4815
+ {
4816
+ section: "plugins",
4817
+ displayName: "Routes page",
4818
+ uid: "page.routes",
4819
+ subCategory: "Pages",
4820
+ pluginName: PLUGIN_ID
4821
+ },
4822
+ {
4823
+ section: "plugins",
4824
+ displayName: "General page",
4825
+ uid: "settings.general",
4826
+ subCategory: "Settings",
4827
+ pluginName: PLUGIN_ID
4828
+ },
4829
+ {
4830
+ section: "plugins",
4831
+ displayName: "Navigation page",
4832
+ uid: "settings.navigation",
4833
+ subCategory: "Settings",
4834
+ pluginName: PLUGIN_ID
4835
+ },
4836
+ {
4837
+ section: "plugins",
4838
+ displayName: "Aside panel",
4839
+ uid: "cm.aside",
4840
+ subCategory: "Content Manager",
4841
+ pluginName: PLUGIN_ID
4842
+ }
4843
+ ];
4844
+ strapi2.admin.services.permission.actionProvider.registerMany(actions);
4845
+ }
4846
+ async function syncConfig(strapi2, enabledContentTypes) {
4763
4847
  const pluginStore = strapi2.store({ type: "plugin", name: PLUGIN_ID });
4764
4848
  const config2 = await pluginStore.get({
4765
4849
  key: "config"
@@ -4784,140 +4868,117 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4784
4868
  if (JSON.stringify(newConfig) !== JSON.stringify(config2)) {
4785
4869
  await pluginStore.set({ key: "config", value: newConfig });
4786
4870
  }
4787
- if (!enabledContentTypes.length) return;
4788
- strapi2.db?.lifecycles.subscribe({
4789
- models: [waNavItem],
4790
- // TODO: is beforeDelete needed?
4791
- async beforeDelete(event) {
4792
- const id = event.params.where["id"];
4793
- if (!id) return;
4794
- try {
4795
- const navItem = await strapi2.db?.query(waNavItem).findOne({
4796
- where: {
4797
- id
4798
- },
4799
- populate: ["route"]
4800
- });
4801
- if (!navItem || !navItem.route) return;
4802
- event.state = navItem.route.id && navItem.route.type === "external" ? { id: navItem.route.id } : null;
4803
- } catch (err) {
4804
- strapi2.log.error(err);
4805
- }
4806
- },
4807
- async afterDelete(event) {
4808
- const { id } = event.state;
4809
- if (!id) return;
4810
- try {
4811
- await strapi2.db?.query(waRoute).delete({
4812
- where: {
4813
- id
4814
- }
4815
- });
4816
- } catch (err) {
4817
- strapi2.log.error(err);
4818
- }
4871
+ return newConfig;
4872
+ }
4873
+ function documentMiddleware(strapi2, enabledContentTypes, config2) {
4874
+ const actions = ["create", "update", "delete"];
4875
+ strapi2.documents.use(async (context, next) => {
4876
+ if (!enabledContentTypes.map((type) => type.uid).includes(context.uid) || !actions.includes(context.action)) {
4877
+ return next();
4819
4878
  }
4820
- });
4821
- strapi2.db?.lifecycles.subscribe({
4822
- models: enabledContentTypes.map((type) => type.uid),
4823
- async beforeCreate(event) {
4824
- if (!event.params.data.webatlas_path) return;
4825
- event.params.data.webatlas_path = transformToUrl(event.params.data.webatlas_path);
4826
- },
4827
- async afterCreate(event) {
4828
- const ctSettings = config2.selectedContentTypes.find((type) => type.uid === event.model.uid);
4829
- const {
4830
- webatlas_path,
4831
- webatlas_override,
4832
- webatlas_parent
4833
- } = event.params.data;
4834
- if (!webatlas_path) return;
4835
- const relatedRoute = await strapi2.db?.query(waRoute).findOne({
4836
- where: {
4837
- relatedDocumentId: event.result.documentId
4838
- }
4879
+ const ctSettings = config2.selectedContentTypes.find((ct) => ct.uid === context.uid);
4880
+ if (context.action === "create") {
4881
+ const data = context.params.data;
4882
+ const { webatlas } = data;
4883
+ const { slug, parentDocumentId, isOverride } = webatlas || {};
4884
+ const transformedSlug = slug ? transformToUrl(slug) : null;
4885
+ if (transformedSlug) {
4886
+ data.webatlas.slug = transformedSlug;
4887
+ }
4888
+ const result2 = await next();
4889
+ if (!transformedSlug) return result2;
4890
+ const existing = await strapi2.db?.query(waRoute).findOne({
4891
+ where: { relatedDocumentId: result2.documentId }
4839
4892
  });
4840
- if (relatedRoute) return;
4893
+ if (existing) return result2;
4841
4894
  let parent = null;
4842
- if (webatlas_parent) {
4843
- try {
4844
- const isValid = await validateRouteDependencies({
4845
- newParentId: webatlas_parent
4895
+ let isValid = false;
4896
+ if (parentDocumentId) {
4897
+ isValid = await validateRouteDependencies({ newParentId: parentDocumentId });
4898
+ if (isValid) {
4899
+ parent = await strapi2.documents(waRoute).findOne({
4900
+ documentId: parentDocumentId
4846
4901
  });
4847
- if (isValid) parent = webatlas_parent;
4848
- } catch (err) {
4849
- strapi2.log.error(`Route dependency validation failed: ${err.message}`);
4850
4902
  }
4851
4903
  }
4852
- const path = await duplicateCheck(transformToUrl(webatlas_path));
4853
- const canonicalPath = await buildCanonicalPath(path, parent);
4854
- const title = event.params.data[ctSettings?.default]?.trim() || path;
4904
+ let rawPath = transformedSlug;
4905
+ if (!isOverride) rawPath = parent ? `${parent.path}/${transformedSlug}` : transformedSlug;
4906
+ const validatedPath = await duplicateCheck(rawPath);
4907
+ if (!validatedPath) throw new Error(`Failed to generate a unique path for slug: ${transformedSlug}`);
4908
+ const singularName = context.contentType.info.singularName;
4909
+ const title = context.params.data[ctSettings?.default]?.trim() || transformedSlug;
4910
+ const canonicalPath = await buildCanonicalPath(transformToUrl(title), isValid ? parent.documentId : null);
4855
4911
  await strapi2.documents(waRoute).create({
4856
4912
  data: {
4857
- relatedContentType: event.model.uid,
4858
- relatedId: event.result.id,
4859
- relatedDocumentId: event.result.documentId,
4860
- slug: path,
4861
- path,
4862
- uidPath: `${event.model.singularName}/${event.result.id}`,
4863
- isOverride: webatlas_override || false,
4913
+ relatedContentType: context.uid,
4914
+ relatedId: result2.id,
4915
+ relatedDocumentId: result2.documentId,
4916
+ slug: transformedSlug,
4917
+ path: validatedPath,
4918
+ uidPath: `${singularName}/${result2.id}`,
4919
+ isOverride: isOverride || false,
4864
4920
  title,
4865
- parent,
4921
+ parent: isValid ? parent?.documentId : null,
4866
4922
  canonicalPath
4867
4923
  }
4868
4924
  });
4869
- },
4870
- async afterUpdate(event) {
4871
- const ctSettings = config2.selectedContentTypes.find((type) => type.uid === event.model.uid);
4872
- const {
4873
- webatlas_path,
4874
- webatlas_override,
4875
- webatlas_parent,
4876
- documentId
4877
- } = event.params.data;
4878
- if (!webatlas_path) return;
4925
+ await strapi2.db?.query(context.uid).updateMany({
4926
+ where: { documentId: result2.documentId },
4927
+ data: { webatlas: { ...webatlas, slug: transformedSlug, path: validatedPath, parentDocumentId: isValid ? parent?.documentId : null } }
4928
+ });
4929
+ return result2;
4930
+ }
4931
+ if (context.action === "update") {
4932
+ const data = context.params.data;
4933
+ const { documentId } = context.params;
4934
+ const { webatlas } = data;
4935
+ const { slug, parentDocumentId, isOverride } = webatlas || {};
4936
+ if (!slug) return;
4879
4937
  const relatedRoute = await strapi2.documents(waRoute).findFirst({
4880
4938
  filters: {
4881
4939
  relatedDocumentId: documentId
4882
4940
  }
4883
4941
  });
4884
4942
  let parent = null;
4885
- if (webatlas_parent) {
4886
- try {
4887
- const isValid = await validateRouteDependencies({
4888
- routeId: relatedRoute ? relatedRoute.documentId : null,
4889
- newParentId: webatlas_parent
4943
+ let isValid = false;
4944
+ if (parentDocumentId) {
4945
+ isValid = await validateRouteDependencies({
4946
+ routeId: relatedRoute ? relatedRoute.documentId : null,
4947
+ newParentId: parentDocumentId
4948
+ });
4949
+ if (isValid) {
4950
+ parent = await strapi2.documents(waRoute).findOne({
4951
+ documentId: parentDocumentId
4890
4952
  });
4891
- if (isValid) {
4892
- parent = await strapi2.documents(waRoute).findOne({
4893
- documentId: webatlas_parent
4894
- });
4895
- }
4896
- } catch (err) {
4897
- strapi2.log.error(`Route dependency validation failed: ${err.message}`);
4898
4953
  }
4899
4954
  }
4900
- const transformedPath = transformToUrl(webatlas_path);
4901
- const rawPath = parent ? `${parent.path}/${transformedPath}` : transformedPath;
4902
- const path = await duplicateCheck(rawPath, relatedRoute ? relatedRoute.documentId : null);
4903
- const canonicalPath = await buildCanonicalPath(transformedPath, parent?.documentId);
4904
- const title = event.params.data[ctSettings?.default]?.trim() || path;
4955
+ const transformedSlug = transformToUrl(slug);
4956
+ let rawPath = transformedSlug;
4957
+ if (!isOverride) rawPath = parent ? `${parent.path}/${transformedSlug}` : transformedSlug;
4958
+ const validatedPath = await duplicateCheck(rawPath, relatedRoute?.documentId ?? null);
4959
+ data.webatlas.path = validatedPath;
4960
+ data.webatlas.slug = transformedSlug;
4961
+ if (relatedRoute) data.relatedRoute = relatedRoute;
4962
+ if (!isValid && parentDocumentId) data.webatlas.parent = null;
4963
+ const result2 = await next();
4964
+ const title = context.params.data[ctSettings?.default]?.trim() || slug;
4965
+ const canonicalPath = isOverride ? relatedRoute.path : await buildCanonicalPath(transformToUrl(title), parent?.documentId);
4905
4966
  const routeData = {
4906
4967
  title,
4907
- path,
4908
- slug: transformedPath,
4909
- isOverride: webatlas_override || false,
4910
- parent: parent?.documentId || null
4968
+ path: validatedPath,
4969
+ slug,
4970
+ isOverride: isOverride || false,
4971
+ parent: parent?.documentId || null,
4972
+ canonicalPath
4911
4973
  };
4912
4974
  let routeDocumentId = relatedRoute?.documentId;
4913
4975
  if (!relatedRoute) {
4914
4976
  const createdRoute = await strapi2.documents(waRoute).create({
4915
4977
  data: {
4916
- relatedContentType: event.model.uid,
4917
- relatedId: event.result.id,
4918
- relatedDocumentId: event.result.documentId,
4919
- uidPath: `${event.model.singularName}/${event.result.id}`,
4920
- canonicalPath,
4978
+ relatedContentType: context.uid,
4979
+ relatedId: result2.id,
4980
+ relatedDocumentId: result2.documentId,
4981
+ uidPath: `${context.contentType.info.singularName}/${result2.id}`,
4921
4982
  ...routeData
4922
4983
  }
4923
4984
  });
@@ -4925,57 +4986,121 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4925
4986
  } else {
4926
4987
  await strapi2.documents(waRoute).update({
4927
4988
  documentId: relatedRoute.documentId,
4928
- data: {
4929
- ...routeData,
4930
- canonicalPath
4931
- }
4989
+ data: routeData
4932
4990
  });
4933
4991
  }
4934
4992
  if (routeDocumentId) {
4935
- await cascadeCanonicalPathUpdates(routeDocumentId, canonicalPath);
4993
+ await cascadePathUpdates({
4994
+ validatedParentPath: validatedPath,
4995
+ parentRouteDocumentId: routeDocumentId,
4996
+ canonicalPath,
4997
+ isOverride
4998
+ });
4936
4999
  }
4937
- },
4938
- async afterDelete(event) {
5000
+ }
5001
+ if (context.action === "delete") {
5002
+ const result2 = await next();
4939
5003
  try {
4940
- await findAndDeleteNavItem(event.result.id, event.model.uid);
5004
+ const relatedDocumentId = context.params.documentId;
5005
+ const deletedRoute = await strapi2.db.query(waRoute).delete({
5006
+ where: { relatedDocumentId },
5007
+ populate: ["navitem"]
5008
+ });
5009
+ if (!deletedRoute?.documentId) return result2;
5010
+ const navItemDocumentIds = Array.from(deletedRoute.navitem?.map((item) => item.documentId));
5011
+ for (const navItemDocumentId of navItemDocumentIds) {
5012
+ await strapi2.documents(waNavItem).delete({ documentId: navItemDocumentId });
5013
+ }
4941
5014
  } catch (err) {
4942
5015
  strapi2.log.error(err);
4943
5016
  }
4944
- },
4945
- async afterDeleteMany(event) {
4946
- const deletedArr = event.params.where["$and"];
4947
- deletedArr.map((item) => {
4948
- const ids = item.id["$in"];
4949
- ids.map(async (id) => {
4950
- await findAndDeleteNavItem(id, event.model.uid);
4951
- });
4952
- });
5017
+ return result2;
4953
5018
  }
5019
+ const result = await next();
5020
+ return result;
4954
5021
  });
4955
- };
4956
- async function findAndDeleteNavItem(relatedId, relatedContentType) {
4957
- if (!relatedId || !relatedContentType) return;
4958
- try {
4959
- const route2 = await strapi.db?.query(waRoute).findOne({
4960
- where: {
4961
- relatedId,
4962
- relatedContentType
5022
+ }
5023
+ function webatlasMiddleware(strapi2) {
5024
+ strapi2.documents.use(async (context, next) => {
5025
+ if (context.uid !== waNavItem) return next();
5026
+ if (context.action === "delete") {
5027
+ let externalRouteDocumentId = null;
5028
+ try {
5029
+ const navItem = await strapi2.db?.query(waNavItem).findOne({
5030
+ where: { documentId: context.params.documentId },
5031
+ populate: ["route"]
5032
+ });
5033
+ if (navItem?.route?.type === "external") {
5034
+ externalRouteDocumentId = navItem.route.documentId;
5035
+ }
5036
+ } catch (err) {
5037
+ strapi2.log.error(err);
4963
5038
  }
4964
- });
4965
- if (!route2?.documentId) return;
4966
- const navItem = await strapi.db?.query(waNavItem).findOne({
4967
- where: {
4968
- route: {
4969
- documentId: route2.documentId
5039
+ const result = await next();
5040
+ if (externalRouteDocumentId) {
5041
+ try {
5042
+ await strapi2.documents(waRoute).delete({ documentId: externalRouteDocumentId });
5043
+ } catch (err) {
5044
+ strapi2.log.error(err);
4970
5045
  }
4971
5046
  }
4972
- });
4973
- await strapi.documents(waRoute).delete({ documentId: route2.documentId });
4974
- if (navItem?.documentId) await strapi.documents(waNavItem).delete({ documentId: navItem.documentId });
4975
- } catch (err) {
4976
- strapi.log.error(err);
4977
- }
5047
+ return result;
5048
+ }
5049
+ return next();
5050
+ });
5051
+ }
5052
+ function contentTypeMiddleware(strapi2) {
5053
+ strapi2.documents.use(async (context, next) => {
5054
+ const pluginOptions = context.contentType?.pluginOptions;
5055
+ if (!pluginOptions?.webatlas?.enabled) {
5056
+ return next();
5057
+ }
5058
+ if (!["findOne", "findMany"].includes(context.action)) {
5059
+ return next();
5060
+ }
5061
+ const result = await next();
5062
+ if (context.action === "findOne") {
5063
+ if (!result || typeof result !== "object" || Array.isArray(result)) return result;
5064
+ const entry = result;
5065
+ const webatlasObj = entry.webatlas || {};
5066
+ const newWebatlasObj = {
5067
+ path: webatlasObj.path || "",
5068
+ slug: webatlasObj.slug || ""
5069
+ };
5070
+ return { ...entry, webatlas: newWebatlasObj };
5071
+ }
5072
+ if (context.action === "findMany") {
5073
+ if (!Array.isArray(result)) return result;
5074
+ return result.map(
5075
+ (entry) => {
5076
+ const webatlasObj = entry.webatlas || {};
5077
+ const newWebatlasObj = {
5078
+ path: webatlasObj.path || "",
5079
+ slug: webatlasObj.slug || ""
5080
+ };
5081
+ return { ...entry, webatlas: newWebatlasObj };
5082
+ }
5083
+ );
5084
+ }
5085
+ return result;
5086
+ });
4978
5087
  }
5088
+ const bootstrap = async ({ strapi: strapi2 }) => {
5089
+ try {
5090
+ await runMigrations(strapi2);
5091
+ registerPermissions(strapi2);
5092
+ const enabledContentTypes = Object.values(strapi2.contentTypes).filter(
5093
+ (type) => type.pluginOptions?.webatlas?.enabled === true
5094
+ );
5095
+ const config2 = await syncConfig(strapi2, enabledContentTypes);
5096
+ if (!enabledContentTypes.length) return;
5097
+ documentMiddleware(strapi2, enabledContentTypes, config2);
5098
+ webatlasMiddleware(strapi2);
5099
+ contentTypeMiddleware(strapi2);
5100
+ } catch (error) {
5101
+ strapi2.log.error(`Bootstrap failed. ${String(error)}`);
5102
+ }
5103
+ };
4979
5104
  const destroy = ({ strapi: strapi2 }) => {
4980
5105
  };
4981
5106
  var _freeGlobal;
@@ -5891,21 +6016,11 @@ const register = ({ strapi: strapi2 }) => {
5891
6016
  visible: true,
5892
6017
  default: null
5893
6018
  };
5894
- set(attributes, "webatlas_path", {
6019
+ set(attributes, "webatlas", {
5895
6020
  ...fieldSettings,
5896
- type: "string",
6021
+ type: "json",
5897
6022
  private: false
5898
6023
  });
5899
- set(attributes, "webatlas_override", {
5900
- ...fieldSettings,
5901
- type: "boolean",
5902
- private: true
5903
- });
5904
- set(attributes, "webatlas_parent", {
5905
- ...fieldSettings,
5906
- type: "string",
5907
- private: true
5908
- });
5909
6024
  });
5910
6025
  };
5911
6026
  const config = {
@@ -6244,9 +6359,9 @@ const client$2 = ({ strapi: strapi2 }) => ({
6244
6359
  },
6245
6360
  async getNavigation(ctx) {
6246
6361
  try {
6247
- const { id, name, documentId, variant } = ctx.query;
6248
- if (!id && !name && !documentId) return ctx.throw(400, "Navigation id, name or documentId is required");
6249
- const navigation2 = await getClientService().getNavigation(id, name, documentId, variant);
6362
+ const { id, name, slug, documentId, variant } = ctx.query;
6363
+ if (!id && !name && !slug && !documentId) return ctx.throw(400, "Navigation id, name, slug or documentId is required");
6364
+ const navigation2 = await getClientService().getNavigation(id, name, slug, documentId, variant);
6250
6365
  if (!navigation2) return ctx.throw(404, "Navigation not found");
6251
6366
  return ctx.send(navigation2);
6252
6367
  } catch (e) {
@@ -6797,23 +6912,32 @@ const client = ({ strapi: strapi2 }) => ({
6797
6912
  return e;
6798
6913
  }
6799
6914
  },
6800
- async getNavigation(id, name, documentId, variant = "nested") {
6915
+ async getNavigation(id, name, slug, documentId, variant = "nested") {
6801
6916
  try {
6802
6917
  let navigation2 = null;
6918
+ const populateObject = ["items", "items.parent", "items.route"];
6803
6919
  const lookupMethods = [
6804
6920
  {
6805
6921
  condition: documentId,
6806
6922
  lookup: () => strapi2.documents(waNavigation).findOne({
6807
6923
  documentId,
6808
- populate: ["items", "items.parent", "items.route"]
6924
+ populate: populateObject
6809
6925
  }),
6810
6926
  name: "documentId"
6811
6927
  },
6928
+ {
6929
+ condition: slug,
6930
+ lookup: () => strapi2.db?.query(waNavigation).findOne({
6931
+ where: { slug },
6932
+ populate: populateObject
6933
+ }),
6934
+ name: "slug"
6935
+ },
6812
6936
  {
6813
6937
  condition: name,
6814
6938
  lookup: () => strapi2.db?.query(waNavigation).findOne({
6815
6939
  where: { name },
6816
- populate: ["items", "items.parent", "items.route"]
6940
+ populate: populateObject
6817
6941
  }),
6818
6942
  name: "name"
6819
6943
  },
@@ -6821,7 +6945,7 @@ const client = ({ strapi: strapi2 }) => ({
6821
6945
  condition: id,
6822
6946
  lookup: () => strapi2.db?.query(waNavigation).findOne({
6823
6947
  where: { id },
6824
- populate: ["items", "items.parent", "items.route"]
6948
+ populate: populateObject
6825
6949
  }),
6826
6950
  name: "id"
6827
6951
  }
@@ -6865,3 +6989,4 @@ const index = {
6865
6989
  export {
6866
6990
  index as default
6867
6991
  };
6992
+ //# sourceMappingURL=index.mjs.map