strapi-plugin-bulk-updater 1.4.6 → 1.4.9

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.
@@ -665,7 +665,7 @@ const csvController = ({ strapi }) => ({
665
665
  },
666
666
  async importCsv(ctx) {
667
667
  try {
668
- const { uid, csvText, mode, publish } = ctx.request.body;
668
+ const { uid, csvText, mode, publish, publishStrategy, unreviewedConsent } = ctx.request.body;
669
669
  if (!uid) {
670
670
  ctx.status = 400;
671
671
  ctx.body = { success: false, error: "Target collection UID is required." };
@@ -680,6 +680,8 @@ const csvController = ({ strapi }) => ({
680
680
  const result = await csvService2.importCsv(uid, csvText, {
681
681
  mode: mode === "update" ? "update" : mode === "create_or_override" ? "create_or_override" : "auto",
682
682
  publish: publish !== false,
683
+ publishStrategy: publishStrategy === "all" ? "all" : "safe",
684
+ unreviewedConsent: Boolean(unreviewedConsent),
683
685
  user: ctx.state.user
684
686
  });
685
687
  ctx.body = result;
@@ -690,7 +692,7 @@ const csvController = ({ strapi }) => ({
690
692
  },
691
693
  async importChunk(ctx) {
692
694
  try {
693
- const { uid, rows, batchId, isFirstChunk, isLastChunk, startRowNumber, mode, publish } = ctx.request.body;
695
+ const { uid, rows, batchId, isFirstChunk, isLastChunk, startRowNumber, mode, publish, publishStrategy, unreviewedConsent } = ctx.request.body;
694
696
  if (!uid) {
695
697
  ctx.status = 400;
696
698
  ctx.body = { success: false, error: "Target collection UID is required." };
@@ -709,6 +711,8 @@ const csvController = ({ strapi }) => ({
709
711
  startRowNumber: typeof startRowNumber === "number" ? startRowNumber : void 0,
710
712
  mode: mode === "update" ? "update" : mode === "create_or_override" ? "create_or_override" : "auto",
711
713
  publish: publish !== false,
714
+ publishStrategy: publishStrategy === "all" ? "all" : "safe",
715
+ unreviewedConsent: Boolean(unreviewedConsent),
712
716
  user: ctx.state.user
713
717
  });
714
718
  ctx.body = result;
@@ -742,7 +746,7 @@ const csvController = ({ strapi }) => ({
742
746
  },
743
747
  async submitBatch(ctx) {
744
748
  try {
745
- const { uid, rows, executionMode, makerNotes, forceImmediate, mode, publish } = ctx.request.body;
749
+ const { uid, rows, executionMode, makerNotes, forceImmediate, mode, publish, publishStrategy, unreviewedConsent, unreviewedCount } = ctx.request.body;
746
750
  if (!uid) {
747
751
  ctx.status = 400;
748
752
  ctx.body = { success: false, error: "Target collection UID is required." };
@@ -760,6 +764,9 @@ const csvController = ({ strapi }) => ({
760
764
  forceImmediate: Boolean(forceImmediate),
761
765
  mode: mode === "update" ? "update" : mode === "create_or_override" ? "create_or_override" : "auto",
762
766
  publish: publish !== false,
767
+ publishStrategy: publishStrategy === "all" ? "all" : "safe",
768
+ unreviewedConsent: Boolean(unreviewedConsent),
769
+ unreviewedCount: typeof unreviewedCount === "number" ? unreviewedCount : 0,
763
770
  user: ctx.state.user
764
771
  });
765
772
  ctx.body = result;
@@ -917,6 +924,60 @@ const modelsCache = /* @__PURE__ */ new Map();
917
924
  const MAX_QUERY_CACHE_SIZE = 500;
918
925
  const CACHE_TTL_MS = 60 * 1e3;
919
926
  let staticSchemaCache = null;
927
+ function formatEntryRecord(entry) {
928
+ if (!entry || typeof entry !== "object") return entry;
929
+ const formatted = {
930
+ id: entry.id,
931
+ documentId: entry.documentId,
932
+ ...entry
933
+ };
934
+ if (entry.updatedAt !== void 0 && formatted.updated_at === void 0) {
935
+ formatted.updated_at = entry.updatedAt;
936
+ }
937
+ if (entry.updated_at !== void 0 && formatted.updatedAt === void 0) {
938
+ formatted.updatedAt = entry.updated_at;
939
+ }
940
+ if (entry.createdAt !== void 0 && formatted.created_at === void 0) {
941
+ formatted.created_at = entry.createdAt;
942
+ }
943
+ if (entry.created_at !== void 0 && formatted.createdAt === void 0) {
944
+ formatted.createdAt = entry.created_at;
945
+ }
946
+ if (entry.publishedAt !== void 0 && formatted.published_at === void 0) {
947
+ formatted.published_at = entry.publishedAt;
948
+ }
949
+ if (entry.published_at !== void 0 && formatted.publishedAt === void 0) {
950
+ formatted.publishedAt = entry.published_at;
951
+ }
952
+ return formatted;
953
+ }
954
+ function resolveContentTypeField(contentType, field) {
955
+ if (!field) return null;
956
+ const attrs = contentType?.attributes || {};
957
+ if (attrs[field]) return field;
958
+ if (field === "updated_at" || field === "updatedAt") {
959
+ return attrs["updated_at"] ? "updated_at" : "updatedAt";
960
+ }
961
+ if (field === "created_at" || field === "createdAt") {
962
+ return attrs["created_at"] ? "created_at" : "createdAt";
963
+ }
964
+ if (field === "published_at" || field === "publishedAt") {
965
+ return attrs["published_at"] ? "published_at" : "publishedAt";
966
+ }
967
+ if (field === "id" || field === "documentId") {
968
+ return field;
969
+ }
970
+ const camel = field.replace(/_([a-z0-9])/g, (_, l) => l.toUpperCase());
971
+ if (attrs[camel]) return camel;
972
+ const snake = field.replace(/([A-Z])/g, "_$1").toLowerCase();
973
+ if (attrs[snake]) return snake;
974
+ const lower = field.toLowerCase().replace(/_/g, "");
975
+ const matched = Object.keys(attrs).find(
976
+ (k) => k.toLowerCase().replace(/_/g, "") === lower
977
+ );
978
+ if (matched) return matched;
979
+ return null;
980
+ }
920
981
  function buildDeepPopulate$1(attributes, allComponents, depth = 0) {
921
982
  if (depth > 5) return void 0;
922
983
  const populate = {};
@@ -1349,7 +1410,9 @@ async function executeBatchInternal(strapi, {
1349
1410
  selectedCollections = [],
1350
1411
  isRegex = false,
1351
1412
  regexPattern = "",
1352
- publishOnApproval = true
1413
+ publishOnApproval = true,
1414
+ publishStrategy = "safe",
1415
+ unreviewedConsent = false
1353
1416
  }, currentUser, existingBatchId) {
1354
1417
  if (!attribute) {
1355
1418
  throw new Error("Attribute name is required.");
@@ -1402,6 +1465,8 @@ async function executeBatchInternal(strapi, {
1402
1465
  failedCount: 0,
1403
1466
  isLive: false,
1404
1467
  publishOnApproval: Boolean(publishOnApproval),
1468
+ publishStrategy,
1469
+ unreviewedConsent,
1405
1470
  results: []
1406
1471
  };
1407
1472
  batchJobsMap.set(batchId, jobState);
@@ -1475,7 +1540,9 @@ async function executeBatchInternal(strapi, {
1475
1540
  }
1476
1541
  });
1477
1542
  const isDraftAndPublishComp = Boolean(contentType?.options?.draftAndPublish);
1478
- if (publishOnApproval && isDraftAndPublishComp) {
1543
+ const isApprovedComp = isDraftAndPublishComp && Boolean(currentEntry?.publishedAt) && currentEntry?.status !== "modified";
1544
+ const shouldPublishComp = isDraftAndPublishComp && Boolean(publishOnApproval) && (publishStrategy === "all" ? true : isApprovedComp);
1545
+ if (shouldPublishComp) {
1479
1546
  try {
1480
1547
  await strapi.documents(target.uid).publish({
1481
1548
  documentId: target.documentId
@@ -1489,7 +1556,7 @@ async function executeBatchInternal(strapi, {
1489
1556
  uid: target.uid,
1490
1557
  documentId: target.documentId,
1491
1558
  userId: currentUser?.id,
1492
- statusOverride: publishOnApproval && isDraftAndPublishComp ? "published" : void 0
1559
+ statusOverride: shouldPublishComp ? "published" : isDraftAndPublishComp ? "draft" : void 0
1493
1560
  });
1494
1561
  const dzDiffs = isRootDz && affectedMatches.length > 0 ? affectedMatches.map((m) => ({
1495
1562
  index: m.index,
@@ -1572,7 +1639,9 @@ async function executeBatchInternal(strapi, {
1572
1639
  }
1573
1640
  });
1574
1641
  const isDraftAndPublishRel = Boolean(contentType?.options?.draftAndPublish);
1575
- if (publishOnApproval && isDraftAndPublishRel) {
1642
+ const isApprovedRel = isDraftAndPublishRel && Boolean(currentEntry?.publishedAt) && currentEntry?.status !== "modified";
1643
+ const shouldPublishRel = isDraftAndPublishRel && Boolean(publishOnApproval) && (publishStrategy === "all" ? true : isApprovedRel);
1644
+ if (shouldPublishRel) {
1576
1645
  try {
1577
1646
  await strapi.documents(target.uid).publish({
1578
1647
  documentId: target.documentId
@@ -1589,7 +1658,7 @@ async function executeBatchInternal(strapi, {
1589
1658
  uid: target.uid,
1590
1659
  documentId: target.documentId,
1591
1660
  userId: currentUser?.id,
1592
- statusOverride: publishOnApproval && isDraftAndPublishRel ? "published" : void 0
1661
+ statusOverride: shouldPublishRel ? "published" : isDraftAndPublishRel ? "draft" : void 0
1593
1662
  });
1594
1663
  return {
1595
1664
  uid: target.uid,
@@ -1615,7 +1684,9 @@ async function executeBatchInternal(strapi, {
1615
1684
  }
1616
1685
  });
1617
1686
  const isDraftAndPublishScalar = Boolean(contentType?.options?.draftAndPublish);
1618
- if (publishOnApproval && isDraftAndPublishScalar) {
1687
+ const isApprovedScalar = isDraftAndPublishScalar && Boolean(currentEntry?.publishedAt) && currentEntry?.status !== "modified";
1688
+ const shouldPublishScalar = isDraftAndPublishScalar && Boolean(publishOnApproval) && (publishStrategy === "all" ? true : isApprovedScalar);
1689
+ if (shouldPublishScalar) {
1619
1690
  try {
1620
1691
  await strapi.documents(target.uid).publish({
1621
1692
  documentId: target.documentId
@@ -1628,7 +1699,7 @@ async function executeBatchInternal(strapi, {
1628
1699
  uid: target.uid,
1629
1700
  documentId: target.documentId,
1630
1701
  userId: currentUser?.id,
1631
- statusOverride: publishOnApproval && isDraftAndPublishScalar ? "published" : void 0
1702
+ statusOverride: shouldPublishScalar ? "published" : isDraftAndPublishScalar ? "draft" : void 0
1632
1703
  });
1633
1704
  return {
1634
1705
  uid: target.uid,
@@ -1715,7 +1786,9 @@ async function executeBatchInternal(strapi, {
1715
1786
  updatedCount,
1716
1787
  skippedCount,
1717
1788
  totalTargets: targetsMap.size,
1718
- publishOnApproval
1789
+ publishOnApproval,
1790
+ publishStrategy,
1791
+ unreviewedConsent
1719
1792
  };
1720
1793
  await strapi.plugin("bulk-updater").service("audit").recordBatch(
1721
1794
  batchSummary,
@@ -1738,7 +1811,8 @@ async function executeBatchInternal(strapi, {
1738
1811
  }
1739
1812
  async function executePublishBatchInternal(strapi, {
1740
1813
  selectedEntries = [],
1741
- selectedCollections = []
1814
+ selectedCollections = [],
1815
+ unreviewedConsent = false
1742
1816
  }, currentUser, existingBatchId) {
1743
1817
  const targetsMap = /* @__PURE__ */ new Map();
1744
1818
  for (const colUid of selectedCollections) {
@@ -1783,6 +1857,7 @@ async function executePublishBatchInternal(strapi, {
1783
1857
  failedCount: 0,
1784
1858
  isLive: false,
1785
1859
  publishOnApproval: true,
1860
+ unreviewedConsent,
1786
1861
  results: []
1787
1862
  };
1788
1863
  batchJobsMap.set(batchId, jobState);
@@ -1880,7 +1955,8 @@ async function executePublishBatchInternal(strapi, {
1880
1955
  updatedCount: publishedCount,
1881
1956
  skippedCount,
1882
1957
  totalTargets: targetsMap.size,
1883
- publishOnApproval: true
1958
+ publishOnApproval: true,
1959
+ unreviewedConsent
1884
1960
  };
1885
1961
  await strapi.plugin("bulk-updater").service("audit").recordBatch(
1886
1962
  batchSummary,
@@ -2139,11 +2215,7 @@ const bulkUpdaterService = ({ strapi }) => {
2139
2215
  entries = [];
2140
2216
  totalCount = 0;
2141
2217
  }
2142
- const formattedEntries = (entries || []).map((entry) => ({
2143
- id: entry.id,
2144
- documentId: entry.documentId,
2145
- ...entry
2146
- }));
2218
+ const formattedEntries = (entries || []).map(formatEntryRecord);
2147
2219
  modelsCache.set(uid, {
2148
2220
  signature,
2149
2221
  totalCount,
@@ -2232,21 +2304,26 @@ const bulkUpdaterService = ({ strapi }) => {
2232
2304
  let isNestedAttr = false;
2233
2305
  let isRootDz = false;
2234
2306
  let parts = [];
2307
+ let resolvedFilterAttr = targetFilterAttr;
2235
2308
  if (targetFilterAttr) {
2236
- const parsed = parseDynamicZonePath(targetFilterAttr);
2237
- if (parsed.rootKey && contentType.attributes?.[parsed.rootKey]?.type === "dynamiczone") {
2238
- isRootDz = true;
2309
+ const resolvedField = resolveContentTypeField(contentType, targetFilterAttr);
2310
+ if (resolvedField) {
2311
+ resolvedFilterAttr = resolvedField;
2239
2312
  colHasAttribute = true;
2240
- isNestedAttr = true;
2241
- parts = parsed.cleanPath.split(".");
2242
- } else if (targetFilterAttr.includes(".") || targetFilterAttr.includes("[")) {
2243
- isNestedAttr = isDeepComponentField(contentType, strapi.components, targetFilterAttr);
2244
- if (isNestedAttr) {
2313
+ } else {
2314
+ const parsed = parseDynamicZonePath(targetFilterAttr);
2315
+ if (parsed.rootKey && contentType.attributes?.[parsed.rootKey]?.type === "dynamiczone") {
2316
+ isRootDz = true;
2245
2317
  colHasAttribute = true;
2318
+ isNestedAttr = true;
2246
2319
  parts = parsed.cleanPath.split(".");
2320
+ } else if (targetFilterAttr.includes(".") || targetFilterAttr.includes("[")) {
2321
+ isNestedAttr = isDeepComponentField(contentType, strapi.components, targetFilterAttr);
2322
+ if (isNestedAttr) {
2323
+ colHasAttribute = true;
2324
+ parts = parsed.cleanPath.split(".");
2325
+ }
2247
2326
  }
2248
- } else if (attrs[targetFilterAttr]) {
2249
- colHasAttribute = true;
2250
2327
  }
2251
2328
  }
2252
2329
  const normOp = (filterOperator || "").toLowerCase().replace(/_/g, "");
@@ -2270,9 +2347,44 @@ const bulkUpdaterService = ({ strapi }) => {
2270
2347
  }
2271
2348
  let condition = void 0;
2272
2349
  const q = (filterQuery || "").trim();
2273
- const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi.components, targetFilterAttr) : attrs[targetFilterAttr];
2350
+ const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi.components, targetFilterAttr) : attrs[resolvedFilterAttr];
2351
+ const isRelation = targetAttrSchema?.type === "relation";
2274
2352
  const isStringLike = ["string", "text", "email", "richtext", "uid"].includes(targetAttrSchema?.type);
2275
- if (normOp === "empty") {
2353
+ if (isRelation) {
2354
+ const targetUid = targetAttrSchema?.target;
2355
+ const targetContentType = targetUid ? strapi.contentTypes[targetUid] : void 0;
2356
+ const targetAttrs = targetContentType?.attributes || {};
2357
+ const targetStringAttrs = Object.keys(targetAttrs).filter(
2358
+ (k) => ["string", "text", "email", "uid"].includes(targetAttrs[k]?.type)
2359
+ );
2360
+ if (normOp === "empty") {
2361
+ condition = { documentId: { $null: true } };
2362
+ } else if (normOp === "notempty") {
2363
+ condition = { documentId: { $notNull: true } };
2364
+ } else if (q !== "") {
2365
+ const orConditions = [];
2366
+ targetStringAttrs.slice(0, 5).forEach((k) => {
2367
+ if (normOp === "equals") {
2368
+ orConditions.push({ [k]: { $eqi: q } });
2369
+ } else if (normOp === "startswith") {
2370
+ orConditions.push({ [k]: { $startsWithi: q } });
2371
+ } else if (normOp === "endswith") {
2372
+ orConditions.push({ [k]: { $endsWithi: q } });
2373
+ } else if (normOp === "notequals") {
2374
+ orConditions.push({ [k]: { $ne: q } });
2375
+ } else if (normOp === "notcontains") {
2376
+ orConditions.push({ [k]: { $notContainsi: q } });
2377
+ } else {
2378
+ orConditions.push({ [k]: { $containsi: q } });
2379
+ }
2380
+ });
2381
+ orConditions.push({ documentId: { $eq: q } });
2382
+ if (/^\d+$/.test(q)) {
2383
+ orConditions.push({ id: { $eq: Number(q) } });
2384
+ }
2385
+ condition = orConditions.length === 1 ? orConditions[0] : { $or: orConditions };
2386
+ }
2387
+ } else if (normOp === "empty") {
2276
2388
  condition = isStringLike ? { $or: [{ $null: true }, { $eq: "" }] } : { $null: true };
2277
2389
  } else if (normOp === "notempty") {
2278
2390
  condition = isStringLike ? { $and: [{ $notNull: true }, { $ne: "" }] } : { $notNull: true };
@@ -2308,13 +2420,13 @@ const bulkUpdaterService = ({ strapi }) => {
2308
2420
  } else {
2309
2421
  if (normOp === "empty" && isStringLike) {
2310
2422
  filters.$or = [
2311
- { [targetFilterAttr]: { $null: true } },
2312
- { [targetFilterAttr]: { $eq: "" } }
2423
+ { [resolvedFilterAttr]: { $null: true } },
2424
+ { [resolvedFilterAttr]: { $eq: "" } }
2313
2425
  ];
2314
2426
  } else if (normOp === "notempty" && isStringLike) {
2315
- filters[targetFilterAttr] = { $notNull: true, $ne: "" };
2427
+ filters[resolvedFilterAttr] = { $notNull: true, $ne: "" };
2316
2428
  } else {
2317
- filters[targetFilterAttr] = condition;
2429
+ filters[resolvedFilterAttr] = condition;
2318
2430
  }
2319
2431
  }
2320
2432
  }
@@ -2356,7 +2468,7 @@ const bulkUpdaterService = ({ strapi }) => {
2356
2468
  displayName: contentType.info?.displayName || uid,
2357
2469
  isSingleType: true,
2358
2470
  draftAndPublish: Boolean(contentType.options?.draftAndPublish),
2359
- entries: singleDoc ? [singleDoc] : [],
2471
+ entries: singleDoc ? [formatEntryRecord(singleDoc)] : [],
2360
2472
  pagination: {
2361
2473
  page: 1,
2362
2474
  pageSize: 1,
@@ -2373,8 +2485,13 @@ const bulkUpdaterService = ({ strapi }) => {
2373
2485
  } else {
2374
2486
  sortClause["createdAt"] = "desc";
2375
2487
  }
2376
- } else if (sortField && contentType.attributes?.[sortField]) {
2377
- sortClause[sortField] = sortOrder;
2488
+ } else if (sortField) {
2489
+ const resolvedField = resolveContentTypeField(contentType, sortField);
2490
+ if (resolvedField) {
2491
+ sortClause[resolvedField] = sortOrder;
2492
+ } else {
2493
+ sortClause["createdAt"] = "desc";
2494
+ }
2378
2495
  } else {
2379
2496
  sortClause["createdAt"] = "desc";
2380
2497
  }
@@ -2392,14 +2509,30 @@ const bulkUpdaterService = ({ strapi }) => {
2392
2509
  const passesDzFilter = (entry) => {
2393
2510
  const matches = getAllNestedValues(entry, targetFilterAttr);
2394
2511
  if (normOp === "empty") {
2395
- return matches.length === 0 || matches.every((m) => m.value === void 0 || m.value === null || String(m.value).trim() === "");
2512
+ return matches.length === 0 || matches.every((m) => {
2513
+ const v = m.value;
2514
+ if (v === void 0 || v === null) return true;
2515
+ if (Array.isArray(v)) return v.length === 0;
2516
+ if (typeof v === "object") return Object.keys(v).length === 0;
2517
+ return String(v).trim() === "";
2518
+ });
2396
2519
  }
2397
2520
  if (normOp === "notempty") {
2398
- return matches.some((m) => m.value !== void 0 && m.value !== null && String(m.value).trim() !== "");
2521
+ return matches.some((m) => {
2522
+ const v = m.value;
2523
+ if (v === void 0 || v === null) return false;
2524
+ if (Array.isArray(v)) return v.length > 0;
2525
+ if (typeof v === "object") return Object.keys(v).length > 0;
2526
+ return String(v).trim() !== "";
2527
+ });
2399
2528
  }
2400
- return matches.some((m) => {
2401
- if (m.value === void 0 || m.value === null) return false;
2402
- const valStr = String(m.value).toLowerCase();
2529
+ const testSingleValue = (v) => {
2530
+ if (v === void 0 || v === null) return false;
2531
+ if (Array.isArray(v)) return v.some(testSingleValue);
2532
+ if (typeof v === "object") {
2533
+ return testSingleValue(v.name) || testSingleValue(v.title) || testSingleValue(v.email) || testSingleValue(v.username) || testSingleValue(v.documentId) || testSingleValue(v.id) || testSingleValue(v.slug);
2534
+ }
2535
+ const valStr = String(v).toLowerCase();
2403
2536
  switch (normOp) {
2404
2537
  case "equals":
2405
2538
  return valStr === qVal;
@@ -2416,7 +2549,8 @@ const bulkUpdaterService = ({ strapi }) => {
2416
2549
  default:
2417
2550
  return true;
2418
2551
  }
2419
- });
2552
+ };
2553
+ return matches.some((m) => testSingleValue(m.value));
2420
2554
  };
2421
2555
  const matchedEntries = (rawEntries || []).filter(passesDzFilter);
2422
2556
  const totalEntries = matchedEntries.length;
@@ -2426,11 +2560,7 @@ const bulkUpdaterService = ({ strapi }) => {
2426
2560
  displayName: contentType.info?.displayName || uid,
2427
2561
  isSingleType: false,
2428
2562
  draftAndPublish: Boolean(contentType.options?.draftAndPublish),
2429
- entries: paginatedEntries.map((entry) => ({
2430
- id: entry.id,
2431
- documentId: entry.documentId,
2432
- ...entry
2433
- })),
2563
+ entries: paginatedEntries.map(formatEntryRecord),
2434
2564
  pagination: {
2435
2565
  page: currentPage,
2436
2566
  pageSize: limit,
@@ -2459,11 +2589,7 @@ const bulkUpdaterService = ({ strapi }) => {
2459
2589
  displayName: contentType.info?.displayName || uid,
2460
2590
  isSingleType: false,
2461
2591
  draftAndPublish: Boolean(contentType.options?.draftAndPublish),
2462
- entries: (entries || []).map((entry) => ({
2463
- id: entry.id,
2464
- documentId: entry.documentId,
2465
- ...entry
2466
- })),
2592
+ entries: (entries || []).map(formatEntryRecord),
2467
2593
  pagination: {
2468
2594
  page: currentPage,
2469
2595
  pageSize: limit,
@@ -2807,7 +2933,10 @@ const bulkUpdaterService = ({ strapi }) => {
2807
2933
  executionMode = "immediate",
2808
2934
  makerNotes = "",
2809
2935
  forceImmediate = false,
2810
- publishOnApproval = true
2936
+ publishOnApproval = true,
2937
+ publishStrategy = "safe",
2938
+ unreviewedConsent = false,
2939
+ unreviewedCount = 0
2811
2940
  }, currentUser) {
2812
2941
  if (!attribute) {
2813
2942
  throw new Error("Attribute name is required.");
@@ -2830,13 +2959,16 @@ const bulkUpdaterService = ({ strapi }) => {
2830
2959
  lastname: currentUser.lastname,
2831
2960
  username: currentUser.username
2832
2961
  } : { email: "admin@system.local" },
2833
- payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
2962
+ payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent, unreviewedCount },
2834
2963
  summary: {
2835
2964
  attribute,
2836
2965
  value,
2837
2966
  totalTargets: summary.totalTargets,
2838
2967
  collectionBreakdown: summary.collectionBreakdown,
2839
- publishOnApproval
2968
+ publishOnApproval,
2969
+ publishStrategy,
2970
+ unreviewedConsent,
2971
+ unreviewedCount
2840
2972
  },
2841
2973
  status: "pending_approval",
2842
2974
  executionMode: "maker_checker",
@@ -2858,6 +2990,9 @@ const bulkUpdaterService = ({ strapi }) => {
2858
2990
  failedCount: 0,
2859
2991
  isLive: false,
2860
2992
  publishOnApproval: Boolean(publishOnApproval),
2993
+ publishStrategy,
2994
+ unreviewedConsent,
2995
+ unreviewedCount,
2861
2996
  makerNotes,
2862
2997
  results: []
2863
2998
  });
@@ -2886,13 +3021,16 @@ const bulkUpdaterService = ({ strapi }) => {
2886
3021
  lastname: currentUser.lastname,
2887
3022
  username: currentUser.username
2888
3023
  } : { email: "admin@system.local" },
2889
- payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
3024
+ payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent, unreviewedCount },
2890
3025
  summary: {
2891
3026
  attribute,
2892
3027
  value,
2893
3028
  totalTargets: summary.totalTargets,
2894
3029
  collectionBreakdown: summary.collectionBreakdown,
2895
- publishOnApproval
3030
+ publishOnApproval,
3031
+ publishStrategy,
3032
+ unreviewedConsent,
3033
+ unreviewedCount
2896
3034
  },
2897
3035
  status: "queued_offpeak",
2898
3036
  executionMode: "queue_offpeak",
@@ -2916,6 +3054,9 @@ const bulkUpdaterService = ({ strapi }) => {
2916
3054
  failedCount: 0,
2917
3055
  isLive: false,
2918
3056
  publishOnApproval: Boolean(publishOnApproval),
3057
+ publishStrategy,
3058
+ unreviewedConsent,
3059
+ unreviewedCount,
2919
3060
  makerNotes,
2920
3061
  results: []
2921
3062
  });
@@ -2945,13 +3086,16 @@ const bulkUpdaterService = ({ strapi }) => {
2945
3086
  lastname: currentUser.lastname,
2946
3087
  username: currentUser.username
2947
3088
  } : { email: "admin@system.local" },
2948
- payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
3089
+ payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent, unreviewedCount },
2949
3090
  summary: {
2950
3091
  attribute,
2951
3092
  value,
2952
3093
  totalTargets: summary.totalTargets,
2953
3094
  collectionBreakdown: summary.collectionBreakdown,
2954
- publishOnApproval
3095
+ publishOnApproval,
3096
+ publishStrategy,
3097
+ unreviewedConsent,
3098
+ unreviewedCount
2955
3099
  },
2956
3100
  status: "queued_offpeak",
2957
3101
  executionMode: "queue_offpeak",
@@ -2975,6 +3119,9 @@ const bulkUpdaterService = ({ strapi }) => {
2975
3119
  failedCount: 0,
2976
3120
  isLive: false,
2977
3121
  publishOnApproval: Boolean(publishOnApproval),
3122
+ publishStrategy,
3123
+ unreviewedConsent,
3124
+ unreviewedCount,
2978
3125
  policyMessage,
2979
3126
  makerNotes: "Automatically queued due to peak business hours restriction.",
2980
3127
  results: []
@@ -2991,7 +3138,7 @@ const bulkUpdaterService = ({ strapi }) => {
2991
3138
  }
2992
3139
  return await executeBatchInternal(
2993
3140
  strapi,
2994
- { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
3141
+ { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent },
2995
3142
  currentUser
2996
3143
  );
2997
3144
  },
@@ -3003,7 +3150,9 @@ const bulkUpdaterService = ({ strapi }) => {
3003
3150
  selectedCollections = [],
3004
3151
  executionMode = "immediate",
3005
3152
  makerNotes = "",
3006
- forceImmediate = false
3153
+ forceImmediate = false,
3154
+ unreviewedConsent = false,
3155
+ unreviewedCount = 0
3007
3156
  }, currentUser) {
3008
3157
  const executionConfig = getExecutionWindowConfig(strapi);
3009
3158
  const { isBusinessHours, policyMessage } = checkBusinessHours(executionConfig);
@@ -3023,13 +3172,15 @@ const bulkUpdaterService = ({ strapi }) => {
3023
3172
  lastname: currentUser.lastname,
3024
3173
  username: currentUser.username
3025
3174
  } : { email: "admin@system.local" },
3026
- payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true },
3175
+ payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true, unreviewedConsent, unreviewedCount },
3027
3176
  summary: {
3028
3177
  attribute: "(publish)",
3029
3178
  value: "published",
3030
3179
  totalTargets: summary.totalTargets,
3031
3180
  collectionBreakdown: summary.collectionBreakdown,
3032
- publishOnApproval: true
3181
+ publishOnApproval: true,
3182
+ unreviewedConsent,
3183
+ unreviewedCount
3033
3184
  },
3034
3185
  status: "pending_approval",
3035
3186
  executionMode: "maker_checker",
@@ -3051,6 +3202,8 @@ const bulkUpdaterService = ({ strapi }) => {
3051
3202
  failedCount: 0,
3052
3203
  isLive: false,
3053
3204
  publishOnApproval: true,
3205
+ unreviewedConsent,
3206
+ unreviewedCount,
3054
3207
  makerNotes,
3055
3208
  results: []
3056
3209
  });
@@ -3079,13 +3232,15 @@ const bulkUpdaterService = ({ strapi }) => {
3079
3232
  lastname: currentUser.lastname,
3080
3233
  username: currentUser.username
3081
3234
  } : { email: "admin@system.local" },
3082
- payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true },
3235
+ payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true, unreviewedConsent, unreviewedCount },
3083
3236
  summary: {
3084
3237
  attribute: "(publish)",
3085
3238
  value: "published",
3086
3239
  totalTargets: summary.totalTargets,
3087
3240
  collectionBreakdown: summary.collectionBreakdown,
3088
- publishOnApproval: true
3241
+ publishOnApproval: true,
3242
+ unreviewedConsent,
3243
+ unreviewedCount
3089
3244
  },
3090
3245
  status: "queued_offpeak",
3091
3246
  executionMode: "queue_offpeak",
@@ -3109,6 +3264,8 @@ const bulkUpdaterService = ({ strapi }) => {
3109
3264
  failedCount: 0,
3110
3265
  isLive: false,
3111
3266
  publishOnApproval: true,
3267
+ unreviewedConsent,
3268
+ unreviewedCount,
3112
3269
  makerNotes,
3113
3270
  results: []
3114
3271
  });
@@ -3138,13 +3295,15 @@ const bulkUpdaterService = ({ strapi }) => {
3138
3295
  lastname: currentUser.lastname,
3139
3296
  username: currentUser.username
3140
3297
  } : { email: "admin@system.local" },
3141
- payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true },
3298
+ payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true, unreviewedConsent, unreviewedCount },
3142
3299
  summary: {
3143
3300
  attribute: "(publish)",
3144
3301
  value: "published",
3145
3302
  totalTargets: summary.totalTargets,
3146
3303
  collectionBreakdown: summary.collectionBreakdown,
3147
- publishOnApproval: true
3304
+ publishOnApproval: true,
3305
+ unreviewedConsent,
3306
+ unreviewedCount
3148
3307
  },
3149
3308
  status: "queued_offpeak",
3150
3309
  executionMode: "queue_offpeak",
@@ -3168,6 +3327,8 @@ const bulkUpdaterService = ({ strapi }) => {
3168
3327
  failedCount: 0,
3169
3328
  isLive: false,
3170
3329
  publishOnApproval: true,
3330
+ unreviewedConsent,
3331
+ unreviewedCount,
3171
3332
  policyMessage,
3172
3333
  makerNotes: "Automatically queued due to peak business hours restriction.",
3173
3334
  results: []
@@ -3184,7 +3345,7 @@ const bulkUpdaterService = ({ strapi }) => {
3184
3345
  }
3185
3346
  return await executePublishBatchInternal(
3186
3347
  strapi,
3187
- { selectedEntries, selectedCollections },
3348
+ { selectedEntries, selectedCollections, unreviewedConsent },
3188
3349
  currentUser
3189
3350
  );
3190
3351
  },
@@ -3718,7 +3879,9 @@ const auditService = ({ strapi }) => ({
3718
3879
  value: batchSummary.value,
3719
3880
  updatedCount: batchSummary.updatedCount,
3720
3881
  skippedCount: batchSummary.skippedCount,
3721
- totalTargets: batchSummary.totalTargets
3882
+ totalTargets: batchSummary.totalTargets,
3883
+ publishStrategy: batchSummary.publishStrategy,
3884
+ unreviewedConsent: batchSummary.unreviewedConsent
3722
3885
  },
3723
3886
  user
3724
3887
  );
@@ -3741,6 +3904,9 @@ const auditService = ({ strapi }) => ({
3741
3904
  updatedCount: batchSummary.updatedCount,
3742
3905
  skippedCount: batchSummary.skippedCount,
3743
3906
  totalTargets: batchSummary.totalTargets,
3907
+ publishOnApproval: batchSummary.publishOnApproval,
3908
+ publishStrategy: batchSummary.publishStrategy,
3909
+ unreviewedConsent: batchSummary.unreviewedConsent,
3744
3910
  diffs: results,
3745
3911
  isRolledBack: false,
3746
3912
  rolledBackAt: null,
@@ -4737,7 +4903,23 @@ function getNestedExportValue(entry, path) {
4737
4903
  return String(item);
4738
4904
  }).filter(Boolean).join("; ");
4739
4905
  }
4740
- current = current[part];
4906
+ if (current && typeof current === "object") {
4907
+ if (current[part] !== void 0) {
4908
+ current = current[part];
4909
+ } else {
4910
+ const camel = part.replace(/_([a-z0-9])/g, (_, l) => l.toUpperCase());
4911
+ const snake = part.replace(/([A-Z])/g, "_$1").toLowerCase();
4912
+ if (current[camel] !== void 0) {
4913
+ current = current[camel];
4914
+ } else if (current[snake] !== void 0) {
4915
+ current = current[snake];
4916
+ } else {
4917
+ current = void 0;
4918
+ }
4919
+ }
4920
+ } else {
4921
+ current = void 0;
4922
+ }
4741
4923
  }
4742
4924
  if (current === null || current === void 0) return "";
4743
4925
  if (typeof current === "object" && current.url) {
@@ -4807,7 +4989,8 @@ async function findExistingDocument(strapi, uid, targetDocId, docLocale) {
4807
4989
  }
4808
4990
  async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
4809
4991
  const attrs = ct.attributes || {};
4810
- const { mode = "auto", publish = true, user } = options;
4992
+ const { mode = "auto", publish = true, publishStrategy = "safe", user } = options;
4993
+ const isDraftAndPublish = Boolean(ct.options?.draftAndPublish);
4811
4994
  const stats = {
4812
4995
  total: rows.length,
4813
4996
  created: 0,
@@ -5022,7 +5205,9 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5022
5205
  };
5023
5206
  if (rowLocale) updateArgs.locale = rowLocale;
5024
5207
  await strapi.documents(uid).update(updateArgs);
5025
- if (publish && ct.options?.draftAndPublish) {
5208
+ const isApproved = isDraftAndPublish && Boolean(existing?.publishedAt) && existing?.status !== "modified";
5209
+ const shouldPublish = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : isApproved);
5210
+ if (shouldPublish) {
5026
5211
  try {
5027
5212
  const pubArgs = { documentId: resolvedDocId };
5028
5213
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5036,7 +5221,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5036
5221
  uid,
5037
5222
  documentId: resolvedDocId,
5038
5223
  userId: user?.id,
5039
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5224
+ statusOverride: shouldPublish ? "published" : isDraftAndPublish ? "draft" : void 0
5040
5225
  });
5041
5226
  } catch {
5042
5227
  }
@@ -5075,7 +5260,9 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5075
5260
  };
5076
5261
  if (rowLocale) updateArgs.locale = rowLocale;
5077
5262
  await strapi.documents(uid).update(updateArgs);
5078
- if (publish && ct.options?.draftAndPublish) {
5263
+ const isApproved = isDraftAndPublish && Boolean(existing?.publishedAt) && existing?.status !== "modified";
5264
+ const shouldPublish = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : isApproved);
5265
+ if (shouldPublish) {
5079
5266
  try {
5080
5267
  const pubArgs = { documentId: resolvedDocId };
5081
5268
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5089,7 +5276,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5089
5276
  uid,
5090
5277
  documentId: resolvedDocId,
5091
5278
  userId: user?.id,
5092
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5279
+ statusOverride: shouldPublish ? "published" : isDraftAndPublish ? "draft" : void 0
5093
5280
  });
5094
5281
  } catch {
5095
5282
  }
@@ -5115,7 +5302,8 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5115
5302
  if (rowLocale) createArgs.locale = rowLocale;
5116
5303
  const created = await strapi.documents(uid).create(createArgs);
5117
5304
  const newDocId = created?.documentId || docId;
5118
- if (publish && ct.options?.draftAndPublish && newDocId) {
5305
+ const shouldPublishNew = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : false);
5306
+ if (shouldPublishNew && newDocId) {
5119
5307
  try {
5120
5308
  const pubArgs = { documentId: newDocId };
5121
5309
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5129,7 +5317,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5129
5317
  uid,
5130
5318
  documentId: newDocId,
5131
5319
  userId: user?.id,
5132
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5320
+ statusOverride: shouldPublishNew ? "published" : isDraftAndPublish ? "draft" : void 0
5133
5321
  });
5134
5322
  } catch {
5135
5323
  }
@@ -5169,7 +5357,9 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5169
5357
  };
5170
5358
  if (rowLocale) updateArgs.locale = rowLocale;
5171
5359
  await strapi.documents(uid).update(updateArgs);
5172
- if (publish && ct.options?.draftAndPublish) {
5360
+ const isApproved = isDraftAndPublish && Boolean(existing?.publishedAt) && existing?.status !== "modified";
5361
+ const shouldPublish = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : isApproved);
5362
+ if (shouldPublish) {
5173
5363
  try {
5174
5364
  const pubArgs = { documentId: resolvedDocId };
5175
5365
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5183,7 +5373,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5183
5373
  uid,
5184
5374
  documentId: resolvedDocId,
5185
5375
  userId: user?.id,
5186
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5376
+ statusOverride: shouldPublish ? "published" : isDraftAndPublish ? "draft" : void 0
5187
5377
  });
5188
5378
  } catch {
5189
5379
  }
@@ -5209,7 +5399,8 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5209
5399
  if (rowLocale) createArgs.locale = rowLocale;
5210
5400
  const created = await strapi.documents(uid).create(createArgs);
5211
5401
  const newDocId = created?.documentId || docId;
5212
- if (publish && ct.options?.draftAndPublish && newDocId) {
5402
+ const shouldPublishNew = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : false);
5403
+ if (shouldPublishNew && newDocId) {
5213
5404
  try {
5214
5405
  const pubArgs = { documentId: newDocId };
5215
5406
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5223,7 +5414,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5223
5414
  uid,
5224
5415
  documentId: newDocId,
5225
5416
  userId: user?.id,
5226
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5417
+ statusOverride: shouldPublishNew ? "published" : isDraftAndPublish ? "draft" : void 0
5227
5418
  });
5228
5419
  } catch {
5229
5420
  }
@@ -5255,7 +5446,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5255
5446
  }
5256
5447
  return stats;
5257
5448
  }
5258
- async function recordCsvAuditLog(strapi, ct, uid, batchId, stats, user) {
5449
+ async function recordCsvAuditLog(strapi, ct, uid, batchId, stats, user, publishOnApproval, publishStrategy, unreviewedConsent) {
5259
5450
  if (stats.updated === 0 && stats.created === 0) return;
5260
5451
  try {
5261
5452
  const auditService2 = strapi.plugin("bulk-updater").service("audit");
@@ -5299,7 +5490,14 @@ async function recordCsvAuditLog(strapi, ct, uid, batchId, stats, user) {
5299
5490
  firstname: userName || "Admin"
5300
5491
  };
5301
5492
  if (typeof auditService2.recordBatch === "function") {
5302
- await auditService2.recordBatch(batchSummary, stats.diffs, userObj);
5493
+ await auditService2.recordBatch(
5494
+ batchSummary,
5495
+ stats.diffs,
5496
+ userObj,
5497
+ publishOnApproval,
5498
+ publishStrategy,
5499
+ unreviewedConsent
5500
+ );
5303
5501
  } else if (typeof auditService2.logOperation === "function") {
5304
5502
  await auditService2.logOperation({
5305
5503
  ...batchSummary,
@@ -5626,7 +5824,9 @@ const csvService = ({ strapi }) => ({
5626
5824
  skipped: 0,
5627
5825
  newWithoutDocId: 0,
5628
5826
  errors: [],
5629
- diffs: []
5827
+ diffs: [],
5828
+ publishStrategy: options.publishStrategy,
5829
+ unreviewedConsent: options.unreviewedConsent
5630
5830
  };
5631
5831
  csvImportSessions.set(batchId, session);
5632
5832
  }
@@ -5640,7 +5840,17 @@ const csvService = ({ strapi }) => ({
5640
5840
  session.errors.push(...chunkStats.errors);
5641
5841
  session.diffs.push(...chunkStats.diffs);
5642
5842
  if (options.isLastChunk) {
5643
- await recordCsvAuditLog(strapi, ct, uid, batchId, session, options.user);
5843
+ await recordCsvAuditLog(
5844
+ strapi,
5845
+ ct,
5846
+ uid,
5847
+ batchId,
5848
+ session,
5849
+ options.user,
5850
+ options.publish !== false,
5851
+ session.publishStrategy || options.publishStrategy,
5852
+ session.unreviewedConsent ?? options.unreviewedConsent
5853
+ );
5644
5854
  try {
5645
5855
  const bulkService = strapi.plugin("bulk-updater").service("bulkUpdater");
5646
5856
  if (bulkService && bulkService.invalidateCollectionCache) {
@@ -5694,7 +5904,17 @@ const csvService = ({ strapi }) => ({
5694
5904
  }
5695
5905
  } catch {
5696
5906
  }
5697
- await recordCsvAuditLog(strapi, ct, uid, batchId, stats, options.user);
5907
+ await recordCsvAuditLog(
5908
+ strapi,
5909
+ ct,
5910
+ uid,
5911
+ batchId,
5912
+ stats,
5913
+ options.user,
5914
+ options.publish !== false,
5915
+ options.publishStrategy,
5916
+ options.unreviewedConsent
5917
+ );
5698
5918
  return {
5699
5919
  success: true,
5700
5920
  batchId,
@@ -5714,7 +5934,17 @@ const csvService = ({ strapi }) => ({
5714
5934
  ...options,
5715
5935
  user: effectiveUser
5716
5936
  });
5717
- await recordCsvAuditLog(strapi, ct, uid, batchId, stats, effectiveUser);
5937
+ await recordCsvAuditLog(
5938
+ strapi,
5939
+ ct,
5940
+ uid,
5941
+ batchId,
5942
+ stats,
5943
+ effectiveUser,
5944
+ options.publish !== false,
5945
+ options.publishStrategy,
5946
+ options.unreviewedConsent
5947
+ );
5718
5948
  try {
5719
5949
  const bulkService = strapi.plugin("bulk-updater").service("bulkUpdater");
5720
5950
  if (bulkService && bulkService.invalidateCollectionCache) {
@@ -5752,6 +5982,15 @@ const csvService = ({ strapi }) => ({
5752
5982
  let validRows = 0;
5753
5983
  let estimatedUpdates = 0;
5754
5984
  let estimatedCreates = 0;
5985
+ const isDraftAndPublish = Boolean(ct.options?.draftAndPublish);
5986
+ const lifecycleStats = {
5987
+ isDraftAndPublish,
5988
+ totalChecked: 0,
5989
+ published: 0,
5990
+ modified: 0,
5991
+ draft: 0,
5992
+ unreviewedCount: 0
5993
+ };
5755
5994
  const errors2 = [];
5756
5995
  const warnings = [];
5757
5996
  for (let i = 0; i < rows.length; i++) {
@@ -5764,6 +6003,19 @@ const csvService = ({ strapi }) => ({
5764
6003
  if (docId) {
5765
6004
  existing = await findExistingDocument(strapi, uid, docId, rowLocale);
5766
6005
  }
6006
+ if (existing && isDraftAndPublish) {
6007
+ lifecycleStats.totalChecked++;
6008
+ const isApproved = Boolean(existing.publishedAt) && existing.status !== "modified";
6009
+ if (isApproved) {
6010
+ lifecycleStats.published++;
6011
+ } else if (existing.status === "modified") {
6012
+ lifecycleStats.modified++;
6013
+ lifecycleStats.unreviewedCount++;
6014
+ } else {
6015
+ lifecycleStats.draft++;
6016
+ lifecycleStats.unreviewedCount++;
6017
+ }
6018
+ }
5767
6019
  if (mode === "update") {
5768
6020
  if (!docId) {
5769
6021
  errors2.push({
@@ -5882,6 +6134,7 @@ const csvService = ({ strapi }) => ({
5882
6134
  validRows,
5883
6135
  estimatedUpdates,
5884
6136
  estimatedCreates,
6137
+ lifecycleStats,
5885
6138
  hasErrors: errors2.length > 0,
5886
6139
  hasWarnings: warnings.length > 0,
5887
6140
  errors: errors2.slice(0, 100),
@@ -5907,6 +6160,9 @@ const csvService = ({ strapi }) => ({
5907
6160
  forceImmediate = false,
5908
6161
  mode = "auto",
5909
6162
  publish = true,
6163
+ publishStrategy = "safe",
6164
+ unreviewedConsent = false,
6165
+ unreviewedCount = 0,
5910
6166
  user
5911
6167
  } = options;
5912
6168
  const executionConfig = getExecutionWindowConfig(strapi);
@@ -5931,22 +6187,33 @@ const csvService = ({ strapi }) => ({
5931
6187
  options: {
5932
6188
  mode,
5933
6189
  publish,
6190
+ publishStrategy,
6191
+ unreviewedConsent,
5934
6192
  user
5935
6193
  },
5936
6194
  attribute: `CSV Import: ${collectionDisplayName}`,
5937
6195
  value: `${rows.length} rows (${mode} mode)`,
5938
- publishOnApproval: publish
6196
+ publishOnApproval: publish,
6197
+ publishStrategy,
6198
+ unreviewedConsent,
6199
+ unreviewedCount
5939
6200
  },
5940
6201
  summary: {
5941
6202
  attribute: `CSV Import: ${collectionDisplayName}`,
5942
6203
  value: `${rows.length} rows (${mode} mode)`,
5943
6204
  totalTargets: rows.length,
5944
6205
  collectionBreakdown: { [collectionDisplayName]: rows.length },
5945
- publishOnApproval: publish
6206
+ publishOnApproval: publish,
6207
+ publishStrategy,
6208
+ unreviewedConsent,
6209
+ unreviewedCount
5946
6210
  },
5947
6211
  status: "pending_approval",
5948
6212
  executionMode: "maker_checker",
5949
- makerNotes
6213
+ makerNotes,
6214
+ publishStrategy,
6215
+ unreviewedConsent,
6216
+ unreviewedCount
5950
6217
  };
5951
6218
  registerPendingBatch(batch, {
5952
6219
  id: batchId,
@@ -5963,6 +6230,9 @@ const csvService = ({ strapi }) => ({
5963
6230
  failedCount: 0,
5964
6231
  isLive: false,
5965
6232
  publishOnApproval: publish,
6233
+ publishStrategy,
6234
+ unreviewedConsent,
6235
+ unreviewedCount,
5966
6236
  makerNotes,
5967
6237
  results: []
5968
6238
  });
@@ -5994,23 +6264,34 @@ const csvService = ({ strapi }) => ({
5994
6264
  options: {
5995
6265
  mode,
5996
6266
  publish,
6267
+ publishStrategy,
6268
+ unreviewedConsent,
5997
6269
  user
5998
6270
  },
5999
6271
  attribute: `CSV Import: ${collectionDisplayName}`,
6000
6272
  value: `${rows.length} rows (${mode} mode)`,
6001
- publishOnApproval: publish
6273
+ publishOnApproval: publish,
6274
+ publishStrategy,
6275
+ unreviewedConsent,
6276
+ unreviewedCount
6002
6277
  },
6003
6278
  summary: {
6004
6279
  attribute: `CSV Import: ${collectionDisplayName}`,
6005
6280
  value: `${rows.length} rows (${mode} mode)`,
6006
6281
  totalTargets: rows.length,
6007
6282
  collectionBreakdown: { [collectionDisplayName]: rows.length },
6008
- publishOnApproval: publish
6283
+ publishOnApproval: publish,
6284
+ publishStrategy,
6285
+ unreviewedConsent,
6286
+ unreviewedCount
6009
6287
  },
6010
6288
  status: "queued_offpeak",
6011
6289
  executionMode: "queue_offpeak",
6012
6290
  scheduledFor: executionConfig.offPeakStartTime,
6013
- makerNotes
6291
+ makerNotes,
6292
+ publishStrategy,
6293
+ unreviewedConsent,
6294
+ unreviewedCount
6014
6295
  };
6015
6296
  registerPendingBatch(batch, {
6016
6297
  id: batchId,
@@ -6028,6 +6309,9 @@ const csvService = ({ strapi }) => ({
6028
6309
  failedCount: 0,
6029
6310
  isLive: false,
6030
6311
  publishOnApproval: publish,
6312
+ publishStrategy,
6313
+ unreviewedConsent,
6314
+ unreviewedCount,
6031
6315
  makerNotes,
6032
6316
  results: []
6033
6317
  });
@@ -6060,23 +6344,34 @@ const csvService = ({ strapi }) => ({
6060
6344
  options: {
6061
6345
  mode,
6062
6346
  publish,
6347
+ publishStrategy,
6348
+ unreviewedConsent,
6063
6349
  user
6064
6350
  },
6065
6351
  attribute: `CSV Import: ${collectionDisplayName}`,
6066
6352
  value: `${rows.length} rows (${mode} mode)`,
6067
- publishOnApproval: publish
6353
+ publishOnApproval: publish,
6354
+ publishStrategy,
6355
+ unreviewedConsent,
6356
+ unreviewedCount
6068
6357
  },
6069
6358
  summary: {
6070
6359
  attribute: `CSV Import: ${collectionDisplayName}`,
6071
6360
  value: `${rows.length} rows (${mode} mode)`,
6072
6361
  totalTargets: rows.length,
6073
6362
  collectionBreakdown: { [collectionDisplayName]: rows.length },
6074
- publishOnApproval: publish
6363
+ publishOnApproval: publish,
6364
+ publishStrategy,
6365
+ unreviewedConsent,
6366
+ unreviewedCount
6075
6367
  },
6076
6368
  status: "queued_offpeak",
6077
6369
  executionMode: "queue_offpeak",
6078
6370
  scheduledFor: executionConfig.offPeakStartTime,
6079
- makerNotes: "Automatically queued due to peak business hours restriction."
6371
+ makerNotes: "Automatically queued due to peak business hours restriction.",
6372
+ publishStrategy,
6373
+ unreviewedConsent,
6374
+ unreviewedCount
6080
6375
  };
6081
6376
  registerPendingBatch(batch, {
6082
6377
  id: batchId,
@@ -6094,6 +6389,9 @@ const csvService = ({ strapi }) => ({
6094
6389
  failedCount: 0,
6095
6390
  isLive: false,
6096
6391
  publishOnApproval: publish,
6392
+ publishStrategy,
6393
+ unreviewedConsent,
6394
+ unreviewedCount,
6097
6395
  policyMessage,
6098
6396
  makerNotes: "Automatically queued due to peak business hours restriction.",
6099
6397
  results: []