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.
@@ -668,7 +668,7 @@ const csvController = ({ strapi }) => ({
668
668
  },
669
669
  async importCsv(ctx) {
670
670
  try {
671
- const { uid, csvText, mode, publish } = ctx.request.body;
671
+ const { uid, csvText, mode, publish, publishStrategy, unreviewedConsent } = ctx.request.body;
672
672
  if (!uid) {
673
673
  ctx.status = 400;
674
674
  ctx.body = { success: false, error: "Target collection UID is required." };
@@ -683,6 +683,8 @@ const csvController = ({ strapi }) => ({
683
683
  const result = await csvService2.importCsv(uid, csvText, {
684
684
  mode: mode === "update" ? "update" : mode === "create_or_override" ? "create_or_override" : "auto",
685
685
  publish: publish !== false,
686
+ publishStrategy: publishStrategy === "all" ? "all" : "safe",
687
+ unreviewedConsent: Boolean(unreviewedConsent),
686
688
  user: ctx.state.user
687
689
  });
688
690
  ctx.body = result;
@@ -693,7 +695,7 @@ const csvController = ({ strapi }) => ({
693
695
  },
694
696
  async importChunk(ctx) {
695
697
  try {
696
- const { uid, rows, batchId, isFirstChunk, isLastChunk, startRowNumber, mode, publish } = ctx.request.body;
698
+ const { uid, rows, batchId, isFirstChunk, isLastChunk, startRowNumber, mode, publish, publishStrategy, unreviewedConsent } = ctx.request.body;
697
699
  if (!uid) {
698
700
  ctx.status = 400;
699
701
  ctx.body = { success: false, error: "Target collection UID is required." };
@@ -712,6 +714,8 @@ const csvController = ({ strapi }) => ({
712
714
  startRowNumber: typeof startRowNumber === "number" ? startRowNumber : void 0,
713
715
  mode: mode === "update" ? "update" : mode === "create_or_override" ? "create_or_override" : "auto",
714
716
  publish: publish !== false,
717
+ publishStrategy: publishStrategy === "all" ? "all" : "safe",
718
+ unreviewedConsent: Boolean(unreviewedConsent),
715
719
  user: ctx.state.user
716
720
  });
717
721
  ctx.body = result;
@@ -745,7 +749,7 @@ const csvController = ({ strapi }) => ({
745
749
  },
746
750
  async submitBatch(ctx) {
747
751
  try {
748
- const { uid, rows, executionMode, makerNotes, forceImmediate, mode, publish } = ctx.request.body;
752
+ const { uid, rows, executionMode, makerNotes, forceImmediate, mode, publish, publishStrategy, unreviewedConsent, unreviewedCount } = ctx.request.body;
749
753
  if (!uid) {
750
754
  ctx.status = 400;
751
755
  ctx.body = { success: false, error: "Target collection UID is required." };
@@ -763,6 +767,9 @@ const csvController = ({ strapi }) => ({
763
767
  forceImmediate: Boolean(forceImmediate),
764
768
  mode: mode === "update" ? "update" : mode === "create_or_override" ? "create_or_override" : "auto",
765
769
  publish: publish !== false,
770
+ publishStrategy: publishStrategy === "all" ? "all" : "safe",
771
+ unreviewedConsent: Boolean(unreviewedConsent),
772
+ unreviewedCount: typeof unreviewedCount === "number" ? unreviewedCount : 0,
766
773
  user: ctx.state.user
767
774
  });
768
775
  ctx.body = result;
@@ -920,6 +927,60 @@ const modelsCache = /* @__PURE__ */ new Map();
920
927
  const MAX_QUERY_CACHE_SIZE = 500;
921
928
  const CACHE_TTL_MS = 60 * 1e3;
922
929
  let staticSchemaCache = null;
930
+ function formatEntryRecord(entry) {
931
+ if (!entry || typeof entry !== "object") return entry;
932
+ const formatted = {
933
+ id: entry.id,
934
+ documentId: entry.documentId,
935
+ ...entry
936
+ };
937
+ if (entry.updatedAt !== void 0 && formatted.updated_at === void 0) {
938
+ formatted.updated_at = entry.updatedAt;
939
+ }
940
+ if (entry.updated_at !== void 0 && formatted.updatedAt === void 0) {
941
+ formatted.updatedAt = entry.updated_at;
942
+ }
943
+ if (entry.createdAt !== void 0 && formatted.created_at === void 0) {
944
+ formatted.created_at = entry.createdAt;
945
+ }
946
+ if (entry.created_at !== void 0 && formatted.createdAt === void 0) {
947
+ formatted.createdAt = entry.created_at;
948
+ }
949
+ if (entry.publishedAt !== void 0 && formatted.published_at === void 0) {
950
+ formatted.published_at = entry.publishedAt;
951
+ }
952
+ if (entry.published_at !== void 0 && formatted.publishedAt === void 0) {
953
+ formatted.publishedAt = entry.published_at;
954
+ }
955
+ return formatted;
956
+ }
957
+ function resolveContentTypeField(contentType, field) {
958
+ if (!field) return null;
959
+ const attrs = contentType?.attributes || {};
960
+ if (attrs[field]) return field;
961
+ if (field === "updated_at" || field === "updatedAt") {
962
+ return attrs["updated_at"] ? "updated_at" : "updatedAt";
963
+ }
964
+ if (field === "created_at" || field === "createdAt") {
965
+ return attrs["created_at"] ? "created_at" : "createdAt";
966
+ }
967
+ if (field === "published_at" || field === "publishedAt") {
968
+ return attrs["published_at"] ? "published_at" : "publishedAt";
969
+ }
970
+ if (field === "id" || field === "documentId") {
971
+ return field;
972
+ }
973
+ const camel = field.replace(/_([a-z0-9])/g, (_, l) => l.toUpperCase());
974
+ if (attrs[camel]) return camel;
975
+ const snake = field.replace(/([A-Z])/g, "_$1").toLowerCase();
976
+ if (attrs[snake]) return snake;
977
+ const lower = field.toLowerCase().replace(/_/g, "");
978
+ const matched = Object.keys(attrs).find(
979
+ (k) => k.toLowerCase().replace(/_/g, "") === lower
980
+ );
981
+ if (matched) return matched;
982
+ return null;
983
+ }
923
984
  function buildDeepPopulate$1(attributes, allComponents, depth = 0) {
924
985
  if (depth > 5) return void 0;
925
986
  const populate = {};
@@ -1352,7 +1413,9 @@ async function executeBatchInternal(strapi, {
1352
1413
  selectedCollections = [],
1353
1414
  isRegex = false,
1354
1415
  regexPattern = "",
1355
- publishOnApproval = true
1416
+ publishOnApproval = true,
1417
+ publishStrategy = "safe",
1418
+ unreviewedConsent = false
1356
1419
  }, currentUser, existingBatchId) {
1357
1420
  if (!attribute) {
1358
1421
  throw new Error("Attribute name is required.");
@@ -1405,6 +1468,8 @@ async function executeBatchInternal(strapi, {
1405
1468
  failedCount: 0,
1406
1469
  isLive: false,
1407
1470
  publishOnApproval: Boolean(publishOnApproval),
1471
+ publishStrategy,
1472
+ unreviewedConsent,
1408
1473
  results: []
1409
1474
  };
1410
1475
  batchJobsMap.set(batchId, jobState);
@@ -1478,7 +1543,9 @@ async function executeBatchInternal(strapi, {
1478
1543
  }
1479
1544
  });
1480
1545
  const isDraftAndPublishComp = Boolean(contentType?.options?.draftAndPublish);
1481
- if (publishOnApproval && isDraftAndPublishComp) {
1546
+ const isApprovedComp = isDraftAndPublishComp && Boolean(currentEntry?.publishedAt) && currentEntry?.status !== "modified";
1547
+ const shouldPublishComp = isDraftAndPublishComp && Boolean(publishOnApproval) && (publishStrategy === "all" ? true : isApprovedComp);
1548
+ if (shouldPublishComp) {
1482
1549
  try {
1483
1550
  await strapi.documents(target.uid).publish({
1484
1551
  documentId: target.documentId
@@ -1492,7 +1559,7 @@ async function executeBatchInternal(strapi, {
1492
1559
  uid: target.uid,
1493
1560
  documentId: target.documentId,
1494
1561
  userId: currentUser?.id,
1495
- statusOverride: publishOnApproval && isDraftAndPublishComp ? "published" : void 0
1562
+ statusOverride: shouldPublishComp ? "published" : isDraftAndPublishComp ? "draft" : void 0
1496
1563
  });
1497
1564
  const dzDiffs = isRootDz && affectedMatches.length > 0 ? affectedMatches.map((m) => ({
1498
1565
  index: m.index,
@@ -1575,7 +1642,9 @@ async function executeBatchInternal(strapi, {
1575
1642
  }
1576
1643
  });
1577
1644
  const isDraftAndPublishRel = Boolean(contentType?.options?.draftAndPublish);
1578
- if (publishOnApproval && isDraftAndPublishRel) {
1645
+ const isApprovedRel = isDraftAndPublishRel && Boolean(currentEntry?.publishedAt) && currentEntry?.status !== "modified";
1646
+ const shouldPublishRel = isDraftAndPublishRel && Boolean(publishOnApproval) && (publishStrategy === "all" ? true : isApprovedRel);
1647
+ if (shouldPublishRel) {
1579
1648
  try {
1580
1649
  await strapi.documents(target.uid).publish({
1581
1650
  documentId: target.documentId
@@ -1592,7 +1661,7 @@ async function executeBatchInternal(strapi, {
1592
1661
  uid: target.uid,
1593
1662
  documentId: target.documentId,
1594
1663
  userId: currentUser?.id,
1595
- statusOverride: publishOnApproval && isDraftAndPublishRel ? "published" : void 0
1664
+ statusOverride: shouldPublishRel ? "published" : isDraftAndPublishRel ? "draft" : void 0
1596
1665
  });
1597
1666
  return {
1598
1667
  uid: target.uid,
@@ -1618,7 +1687,9 @@ async function executeBatchInternal(strapi, {
1618
1687
  }
1619
1688
  });
1620
1689
  const isDraftAndPublishScalar = Boolean(contentType?.options?.draftAndPublish);
1621
- if (publishOnApproval && isDraftAndPublishScalar) {
1690
+ const isApprovedScalar = isDraftAndPublishScalar && Boolean(currentEntry?.publishedAt) && currentEntry?.status !== "modified";
1691
+ const shouldPublishScalar = isDraftAndPublishScalar && Boolean(publishOnApproval) && (publishStrategy === "all" ? true : isApprovedScalar);
1692
+ if (shouldPublishScalar) {
1622
1693
  try {
1623
1694
  await strapi.documents(target.uid).publish({
1624
1695
  documentId: target.documentId
@@ -1631,7 +1702,7 @@ async function executeBatchInternal(strapi, {
1631
1702
  uid: target.uid,
1632
1703
  documentId: target.documentId,
1633
1704
  userId: currentUser?.id,
1634
- statusOverride: publishOnApproval && isDraftAndPublishScalar ? "published" : void 0
1705
+ statusOverride: shouldPublishScalar ? "published" : isDraftAndPublishScalar ? "draft" : void 0
1635
1706
  });
1636
1707
  return {
1637
1708
  uid: target.uid,
@@ -1718,7 +1789,9 @@ async function executeBatchInternal(strapi, {
1718
1789
  updatedCount,
1719
1790
  skippedCount,
1720
1791
  totalTargets: targetsMap.size,
1721
- publishOnApproval
1792
+ publishOnApproval,
1793
+ publishStrategy,
1794
+ unreviewedConsent
1722
1795
  };
1723
1796
  await strapi.plugin("bulk-updater").service("audit").recordBatch(
1724
1797
  batchSummary,
@@ -1741,7 +1814,8 @@ async function executeBatchInternal(strapi, {
1741
1814
  }
1742
1815
  async function executePublishBatchInternal(strapi, {
1743
1816
  selectedEntries = [],
1744
- selectedCollections = []
1817
+ selectedCollections = [],
1818
+ unreviewedConsent = false
1745
1819
  }, currentUser, existingBatchId) {
1746
1820
  const targetsMap = /* @__PURE__ */ new Map();
1747
1821
  for (const colUid of selectedCollections) {
@@ -1786,6 +1860,7 @@ async function executePublishBatchInternal(strapi, {
1786
1860
  failedCount: 0,
1787
1861
  isLive: false,
1788
1862
  publishOnApproval: true,
1863
+ unreviewedConsent,
1789
1864
  results: []
1790
1865
  };
1791
1866
  batchJobsMap.set(batchId, jobState);
@@ -1883,7 +1958,8 @@ async function executePublishBatchInternal(strapi, {
1883
1958
  updatedCount: publishedCount,
1884
1959
  skippedCount,
1885
1960
  totalTargets: targetsMap.size,
1886
- publishOnApproval: true
1961
+ publishOnApproval: true,
1962
+ unreviewedConsent
1887
1963
  };
1888
1964
  await strapi.plugin("bulk-updater").service("audit").recordBatch(
1889
1965
  batchSummary,
@@ -2142,11 +2218,7 @@ const bulkUpdaterService = ({ strapi }) => {
2142
2218
  entries = [];
2143
2219
  totalCount = 0;
2144
2220
  }
2145
- const formattedEntries = (entries || []).map((entry) => ({
2146
- id: entry.id,
2147
- documentId: entry.documentId,
2148
- ...entry
2149
- }));
2221
+ const formattedEntries = (entries || []).map(formatEntryRecord);
2150
2222
  modelsCache.set(uid, {
2151
2223
  signature,
2152
2224
  totalCount,
@@ -2235,21 +2307,26 @@ const bulkUpdaterService = ({ strapi }) => {
2235
2307
  let isNestedAttr = false;
2236
2308
  let isRootDz = false;
2237
2309
  let parts = [];
2310
+ let resolvedFilterAttr = targetFilterAttr;
2238
2311
  if (targetFilterAttr) {
2239
- const parsed = parseDynamicZonePath(targetFilterAttr);
2240
- if (parsed.rootKey && contentType.attributes?.[parsed.rootKey]?.type === "dynamiczone") {
2241
- isRootDz = true;
2312
+ const resolvedField = resolveContentTypeField(contentType, targetFilterAttr);
2313
+ if (resolvedField) {
2314
+ resolvedFilterAttr = resolvedField;
2242
2315
  colHasAttribute = true;
2243
- isNestedAttr = true;
2244
- parts = parsed.cleanPath.split(".");
2245
- } else if (targetFilterAttr.includes(".") || targetFilterAttr.includes("[")) {
2246
- isNestedAttr = isDeepComponentField(contentType, strapi.components, targetFilterAttr);
2247
- if (isNestedAttr) {
2316
+ } else {
2317
+ const parsed = parseDynamicZonePath(targetFilterAttr);
2318
+ if (parsed.rootKey && contentType.attributes?.[parsed.rootKey]?.type === "dynamiczone") {
2319
+ isRootDz = true;
2248
2320
  colHasAttribute = true;
2321
+ isNestedAttr = true;
2249
2322
  parts = parsed.cleanPath.split(".");
2323
+ } else if (targetFilterAttr.includes(".") || targetFilterAttr.includes("[")) {
2324
+ isNestedAttr = isDeepComponentField(contentType, strapi.components, targetFilterAttr);
2325
+ if (isNestedAttr) {
2326
+ colHasAttribute = true;
2327
+ parts = parsed.cleanPath.split(".");
2328
+ }
2250
2329
  }
2251
- } else if (attrs[targetFilterAttr]) {
2252
- colHasAttribute = true;
2253
2330
  }
2254
2331
  }
2255
2332
  const normOp = (filterOperator || "").toLowerCase().replace(/_/g, "");
@@ -2273,9 +2350,44 @@ const bulkUpdaterService = ({ strapi }) => {
2273
2350
  }
2274
2351
  let condition = void 0;
2275
2352
  const q = (filterQuery || "").trim();
2276
- const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi.components, targetFilterAttr) : attrs[targetFilterAttr];
2353
+ const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi.components, targetFilterAttr) : attrs[resolvedFilterAttr];
2354
+ const isRelation = targetAttrSchema?.type === "relation";
2277
2355
  const isStringLike = ["string", "text", "email", "richtext", "uid"].includes(targetAttrSchema?.type);
2278
- if (normOp === "empty") {
2356
+ if (isRelation) {
2357
+ const targetUid = targetAttrSchema?.target;
2358
+ const targetContentType = targetUid ? strapi.contentTypes[targetUid] : void 0;
2359
+ const targetAttrs = targetContentType?.attributes || {};
2360
+ const targetStringAttrs = Object.keys(targetAttrs).filter(
2361
+ (k) => ["string", "text", "email", "uid"].includes(targetAttrs[k]?.type)
2362
+ );
2363
+ if (normOp === "empty") {
2364
+ condition = { documentId: { $null: true } };
2365
+ } else if (normOp === "notempty") {
2366
+ condition = { documentId: { $notNull: true } };
2367
+ } else if (q !== "") {
2368
+ const orConditions = [];
2369
+ targetStringAttrs.slice(0, 5).forEach((k) => {
2370
+ if (normOp === "equals") {
2371
+ orConditions.push({ [k]: { $eqi: q } });
2372
+ } else if (normOp === "startswith") {
2373
+ orConditions.push({ [k]: { $startsWithi: q } });
2374
+ } else if (normOp === "endswith") {
2375
+ orConditions.push({ [k]: { $endsWithi: q } });
2376
+ } else if (normOp === "notequals") {
2377
+ orConditions.push({ [k]: { $ne: q } });
2378
+ } else if (normOp === "notcontains") {
2379
+ orConditions.push({ [k]: { $notContainsi: q } });
2380
+ } else {
2381
+ orConditions.push({ [k]: { $containsi: q } });
2382
+ }
2383
+ });
2384
+ orConditions.push({ documentId: { $eq: q } });
2385
+ if (/^\d+$/.test(q)) {
2386
+ orConditions.push({ id: { $eq: Number(q) } });
2387
+ }
2388
+ condition = orConditions.length === 1 ? orConditions[0] : { $or: orConditions };
2389
+ }
2390
+ } else if (normOp === "empty") {
2279
2391
  condition = isStringLike ? { $or: [{ $null: true }, { $eq: "" }] } : { $null: true };
2280
2392
  } else if (normOp === "notempty") {
2281
2393
  condition = isStringLike ? { $and: [{ $notNull: true }, { $ne: "" }] } : { $notNull: true };
@@ -2311,13 +2423,13 @@ const bulkUpdaterService = ({ strapi }) => {
2311
2423
  } else {
2312
2424
  if (normOp === "empty" && isStringLike) {
2313
2425
  filters.$or = [
2314
- { [targetFilterAttr]: { $null: true } },
2315
- { [targetFilterAttr]: { $eq: "" } }
2426
+ { [resolvedFilterAttr]: { $null: true } },
2427
+ { [resolvedFilterAttr]: { $eq: "" } }
2316
2428
  ];
2317
2429
  } else if (normOp === "notempty" && isStringLike) {
2318
- filters[targetFilterAttr] = { $notNull: true, $ne: "" };
2430
+ filters[resolvedFilterAttr] = { $notNull: true, $ne: "" };
2319
2431
  } else {
2320
- filters[targetFilterAttr] = condition;
2432
+ filters[resolvedFilterAttr] = condition;
2321
2433
  }
2322
2434
  }
2323
2435
  }
@@ -2359,7 +2471,7 @@ const bulkUpdaterService = ({ strapi }) => {
2359
2471
  displayName: contentType.info?.displayName || uid,
2360
2472
  isSingleType: true,
2361
2473
  draftAndPublish: Boolean(contentType.options?.draftAndPublish),
2362
- entries: singleDoc ? [singleDoc] : [],
2474
+ entries: singleDoc ? [formatEntryRecord(singleDoc)] : [],
2363
2475
  pagination: {
2364
2476
  page: 1,
2365
2477
  pageSize: 1,
@@ -2376,8 +2488,13 @@ const bulkUpdaterService = ({ strapi }) => {
2376
2488
  } else {
2377
2489
  sortClause["createdAt"] = "desc";
2378
2490
  }
2379
- } else if (sortField && contentType.attributes?.[sortField]) {
2380
- sortClause[sortField] = sortOrder;
2491
+ } else if (sortField) {
2492
+ const resolvedField = resolveContentTypeField(contentType, sortField);
2493
+ if (resolvedField) {
2494
+ sortClause[resolvedField] = sortOrder;
2495
+ } else {
2496
+ sortClause["createdAt"] = "desc";
2497
+ }
2381
2498
  } else {
2382
2499
  sortClause["createdAt"] = "desc";
2383
2500
  }
@@ -2395,14 +2512,30 @@ const bulkUpdaterService = ({ strapi }) => {
2395
2512
  const passesDzFilter = (entry) => {
2396
2513
  const matches = getAllNestedValues(entry, targetFilterAttr);
2397
2514
  if (normOp === "empty") {
2398
- return matches.length === 0 || matches.every((m) => m.value === void 0 || m.value === null || String(m.value).trim() === "");
2515
+ return matches.length === 0 || matches.every((m) => {
2516
+ const v = m.value;
2517
+ if (v === void 0 || v === null) return true;
2518
+ if (Array.isArray(v)) return v.length === 0;
2519
+ if (typeof v === "object") return Object.keys(v).length === 0;
2520
+ return String(v).trim() === "";
2521
+ });
2399
2522
  }
2400
2523
  if (normOp === "notempty") {
2401
- return matches.some((m) => m.value !== void 0 && m.value !== null && String(m.value).trim() !== "");
2524
+ return matches.some((m) => {
2525
+ const v = m.value;
2526
+ if (v === void 0 || v === null) return false;
2527
+ if (Array.isArray(v)) return v.length > 0;
2528
+ if (typeof v === "object") return Object.keys(v).length > 0;
2529
+ return String(v).trim() !== "";
2530
+ });
2402
2531
  }
2403
- return matches.some((m) => {
2404
- if (m.value === void 0 || m.value === null) return false;
2405
- const valStr = String(m.value).toLowerCase();
2532
+ const testSingleValue = (v) => {
2533
+ if (v === void 0 || v === null) return false;
2534
+ if (Array.isArray(v)) return v.some(testSingleValue);
2535
+ if (typeof v === "object") {
2536
+ return testSingleValue(v.name) || testSingleValue(v.title) || testSingleValue(v.email) || testSingleValue(v.username) || testSingleValue(v.documentId) || testSingleValue(v.id) || testSingleValue(v.slug);
2537
+ }
2538
+ const valStr = String(v).toLowerCase();
2406
2539
  switch (normOp) {
2407
2540
  case "equals":
2408
2541
  return valStr === qVal;
@@ -2419,7 +2552,8 @@ const bulkUpdaterService = ({ strapi }) => {
2419
2552
  default:
2420
2553
  return true;
2421
2554
  }
2422
- });
2555
+ };
2556
+ return matches.some((m) => testSingleValue(m.value));
2423
2557
  };
2424
2558
  const matchedEntries = (rawEntries || []).filter(passesDzFilter);
2425
2559
  const totalEntries = matchedEntries.length;
@@ -2429,11 +2563,7 @@ const bulkUpdaterService = ({ strapi }) => {
2429
2563
  displayName: contentType.info?.displayName || uid,
2430
2564
  isSingleType: false,
2431
2565
  draftAndPublish: Boolean(contentType.options?.draftAndPublish),
2432
- entries: paginatedEntries.map((entry) => ({
2433
- id: entry.id,
2434
- documentId: entry.documentId,
2435
- ...entry
2436
- })),
2566
+ entries: paginatedEntries.map(formatEntryRecord),
2437
2567
  pagination: {
2438
2568
  page: currentPage,
2439
2569
  pageSize: limit,
@@ -2462,11 +2592,7 @@ const bulkUpdaterService = ({ strapi }) => {
2462
2592
  displayName: contentType.info?.displayName || uid,
2463
2593
  isSingleType: false,
2464
2594
  draftAndPublish: Boolean(contentType.options?.draftAndPublish),
2465
- entries: (entries || []).map((entry) => ({
2466
- id: entry.id,
2467
- documentId: entry.documentId,
2468
- ...entry
2469
- })),
2595
+ entries: (entries || []).map(formatEntryRecord),
2470
2596
  pagination: {
2471
2597
  page: currentPage,
2472
2598
  pageSize: limit,
@@ -2810,7 +2936,10 @@ const bulkUpdaterService = ({ strapi }) => {
2810
2936
  executionMode = "immediate",
2811
2937
  makerNotes = "",
2812
2938
  forceImmediate = false,
2813
- publishOnApproval = true
2939
+ publishOnApproval = true,
2940
+ publishStrategy = "safe",
2941
+ unreviewedConsent = false,
2942
+ unreviewedCount = 0
2814
2943
  }, currentUser) {
2815
2944
  if (!attribute) {
2816
2945
  throw new Error("Attribute name is required.");
@@ -2833,13 +2962,16 @@ const bulkUpdaterService = ({ strapi }) => {
2833
2962
  lastname: currentUser.lastname,
2834
2963
  username: currentUser.username
2835
2964
  } : { email: "admin@system.local" },
2836
- payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
2965
+ payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent, unreviewedCount },
2837
2966
  summary: {
2838
2967
  attribute,
2839
2968
  value,
2840
2969
  totalTargets: summary.totalTargets,
2841
2970
  collectionBreakdown: summary.collectionBreakdown,
2842
- publishOnApproval
2971
+ publishOnApproval,
2972
+ publishStrategy,
2973
+ unreviewedConsent,
2974
+ unreviewedCount
2843
2975
  },
2844
2976
  status: "pending_approval",
2845
2977
  executionMode: "maker_checker",
@@ -2861,6 +2993,9 @@ const bulkUpdaterService = ({ strapi }) => {
2861
2993
  failedCount: 0,
2862
2994
  isLive: false,
2863
2995
  publishOnApproval: Boolean(publishOnApproval),
2996
+ publishStrategy,
2997
+ unreviewedConsent,
2998
+ unreviewedCount,
2864
2999
  makerNotes,
2865
3000
  results: []
2866
3001
  });
@@ -2889,13 +3024,16 @@ const bulkUpdaterService = ({ strapi }) => {
2889
3024
  lastname: currentUser.lastname,
2890
3025
  username: currentUser.username
2891
3026
  } : { email: "admin@system.local" },
2892
- payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
3027
+ payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent, unreviewedCount },
2893
3028
  summary: {
2894
3029
  attribute,
2895
3030
  value,
2896
3031
  totalTargets: summary.totalTargets,
2897
3032
  collectionBreakdown: summary.collectionBreakdown,
2898
- publishOnApproval
3033
+ publishOnApproval,
3034
+ publishStrategy,
3035
+ unreviewedConsent,
3036
+ unreviewedCount
2899
3037
  },
2900
3038
  status: "queued_offpeak",
2901
3039
  executionMode: "queue_offpeak",
@@ -2919,6 +3057,9 @@ const bulkUpdaterService = ({ strapi }) => {
2919
3057
  failedCount: 0,
2920
3058
  isLive: false,
2921
3059
  publishOnApproval: Boolean(publishOnApproval),
3060
+ publishStrategy,
3061
+ unreviewedConsent,
3062
+ unreviewedCount,
2922
3063
  makerNotes,
2923
3064
  results: []
2924
3065
  });
@@ -2948,13 +3089,16 @@ const bulkUpdaterService = ({ strapi }) => {
2948
3089
  lastname: currentUser.lastname,
2949
3090
  username: currentUser.username
2950
3091
  } : { email: "admin@system.local" },
2951
- payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
3092
+ payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent, unreviewedCount },
2952
3093
  summary: {
2953
3094
  attribute,
2954
3095
  value,
2955
3096
  totalTargets: summary.totalTargets,
2956
3097
  collectionBreakdown: summary.collectionBreakdown,
2957
- publishOnApproval
3098
+ publishOnApproval,
3099
+ publishStrategy,
3100
+ unreviewedConsent,
3101
+ unreviewedCount
2958
3102
  },
2959
3103
  status: "queued_offpeak",
2960
3104
  executionMode: "queue_offpeak",
@@ -2978,6 +3122,9 @@ const bulkUpdaterService = ({ strapi }) => {
2978
3122
  failedCount: 0,
2979
3123
  isLive: false,
2980
3124
  publishOnApproval: Boolean(publishOnApproval),
3125
+ publishStrategy,
3126
+ unreviewedConsent,
3127
+ unreviewedCount,
2981
3128
  policyMessage,
2982
3129
  makerNotes: "Automatically queued due to peak business hours restriction.",
2983
3130
  results: []
@@ -2994,7 +3141,7 @@ const bulkUpdaterService = ({ strapi }) => {
2994
3141
  }
2995
3142
  return await executeBatchInternal(
2996
3143
  strapi,
2997
- { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },
3144
+ { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval, publishStrategy, unreviewedConsent },
2998
3145
  currentUser
2999
3146
  );
3000
3147
  },
@@ -3006,7 +3153,9 @@ const bulkUpdaterService = ({ strapi }) => {
3006
3153
  selectedCollections = [],
3007
3154
  executionMode = "immediate",
3008
3155
  makerNotes = "",
3009
- forceImmediate = false
3156
+ forceImmediate = false,
3157
+ unreviewedConsent = false,
3158
+ unreviewedCount = 0
3010
3159
  }, currentUser) {
3011
3160
  const executionConfig = getExecutionWindowConfig(strapi);
3012
3161
  const { isBusinessHours, policyMessage } = checkBusinessHours(executionConfig);
@@ -3026,13 +3175,15 @@ const bulkUpdaterService = ({ strapi }) => {
3026
3175
  lastname: currentUser.lastname,
3027
3176
  username: currentUser.username
3028
3177
  } : { email: "admin@system.local" },
3029
- payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true },
3178
+ payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true, unreviewedConsent, unreviewedCount },
3030
3179
  summary: {
3031
3180
  attribute: "(publish)",
3032
3181
  value: "published",
3033
3182
  totalTargets: summary.totalTargets,
3034
3183
  collectionBreakdown: summary.collectionBreakdown,
3035
- publishOnApproval: true
3184
+ publishOnApproval: true,
3185
+ unreviewedConsent,
3186
+ unreviewedCount
3036
3187
  },
3037
3188
  status: "pending_approval",
3038
3189
  executionMode: "maker_checker",
@@ -3054,6 +3205,8 @@ const bulkUpdaterService = ({ strapi }) => {
3054
3205
  failedCount: 0,
3055
3206
  isLive: false,
3056
3207
  publishOnApproval: true,
3208
+ unreviewedConsent,
3209
+ unreviewedCount,
3057
3210
  makerNotes,
3058
3211
  results: []
3059
3212
  });
@@ -3082,13 +3235,15 @@ const bulkUpdaterService = ({ strapi }) => {
3082
3235
  lastname: currentUser.lastname,
3083
3236
  username: currentUser.username
3084
3237
  } : { email: "admin@system.local" },
3085
- payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true },
3238
+ payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true, unreviewedConsent, unreviewedCount },
3086
3239
  summary: {
3087
3240
  attribute: "(publish)",
3088
3241
  value: "published",
3089
3242
  totalTargets: summary.totalTargets,
3090
3243
  collectionBreakdown: summary.collectionBreakdown,
3091
- publishOnApproval: true
3244
+ publishOnApproval: true,
3245
+ unreviewedConsent,
3246
+ unreviewedCount
3092
3247
  },
3093
3248
  status: "queued_offpeak",
3094
3249
  executionMode: "queue_offpeak",
@@ -3112,6 +3267,8 @@ const bulkUpdaterService = ({ strapi }) => {
3112
3267
  failedCount: 0,
3113
3268
  isLive: false,
3114
3269
  publishOnApproval: true,
3270
+ unreviewedConsent,
3271
+ unreviewedCount,
3115
3272
  makerNotes,
3116
3273
  results: []
3117
3274
  });
@@ -3141,13 +3298,15 @@ const bulkUpdaterService = ({ strapi }) => {
3141
3298
  lastname: currentUser.lastname,
3142
3299
  username: currentUser.username
3143
3300
  } : { email: "admin@system.local" },
3144
- payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true },
3301
+ payload: { action: "publish", attribute: "(publish)", value: "published", selectedEntries, selectedCollections, publishOnApproval: true, unreviewedConsent, unreviewedCount },
3145
3302
  summary: {
3146
3303
  attribute: "(publish)",
3147
3304
  value: "published",
3148
3305
  totalTargets: summary.totalTargets,
3149
3306
  collectionBreakdown: summary.collectionBreakdown,
3150
- publishOnApproval: true
3307
+ publishOnApproval: true,
3308
+ unreviewedConsent,
3309
+ unreviewedCount
3151
3310
  },
3152
3311
  status: "queued_offpeak",
3153
3312
  executionMode: "queue_offpeak",
@@ -3171,6 +3330,8 @@ const bulkUpdaterService = ({ strapi }) => {
3171
3330
  failedCount: 0,
3172
3331
  isLive: false,
3173
3332
  publishOnApproval: true,
3333
+ unreviewedConsent,
3334
+ unreviewedCount,
3174
3335
  policyMessage,
3175
3336
  makerNotes: "Automatically queued due to peak business hours restriction.",
3176
3337
  results: []
@@ -3187,7 +3348,7 @@ const bulkUpdaterService = ({ strapi }) => {
3187
3348
  }
3188
3349
  return await executePublishBatchInternal(
3189
3350
  strapi,
3190
- { selectedEntries, selectedCollections },
3351
+ { selectedEntries, selectedCollections, unreviewedConsent },
3191
3352
  currentUser
3192
3353
  );
3193
3354
  },
@@ -3721,7 +3882,9 @@ const auditService = ({ strapi }) => ({
3721
3882
  value: batchSummary.value,
3722
3883
  updatedCount: batchSummary.updatedCount,
3723
3884
  skippedCount: batchSummary.skippedCount,
3724
- totalTargets: batchSummary.totalTargets
3885
+ totalTargets: batchSummary.totalTargets,
3886
+ publishStrategy: batchSummary.publishStrategy,
3887
+ unreviewedConsent: batchSummary.unreviewedConsent
3725
3888
  },
3726
3889
  user
3727
3890
  );
@@ -3744,6 +3907,9 @@ const auditService = ({ strapi }) => ({
3744
3907
  updatedCount: batchSummary.updatedCount,
3745
3908
  skippedCount: batchSummary.skippedCount,
3746
3909
  totalTargets: batchSummary.totalTargets,
3910
+ publishOnApproval: batchSummary.publishOnApproval,
3911
+ publishStrategy: batchSummary.publishStrategy,
3912
+ unreviewedConsent: batchSummary.unreviewedConsent,
3747
3913
  diffs: results,
3748
3914
  isRolledBack: false,
3749
3915
  rolledBackAt: null,
@@ -4740,7 +4906,23 @@ function getNestedExportValue(entry, path) {
4740
4906
  return String(item);
4741
4907
  }).filter(Boolean).join("; ");
4742
4908
  }
4743
- current = current[part];
4909
+ if (current && typeof current === "object") {
4910
+ if (current[part] !== void 0) {
4911
+ current = current[part];
4912
+ } else {
4913
+ const camel = part.replace(/_([a-z0-9])/g, (_, l) => l.toUpperCase());
4914
+ const snake = part.replace(/([A-Z])/g, "_$1").toLowerCase();
4915
+ if (current[camel] !== void 0) {
4916
+ current = current[camel];
4917
+ } else if (current[snake] !== void 0) {
4918
+ current = current[snake];
4919
+ } else {
4920
+ current = void 0;
4921
+ }
4922
+ }
4923
+ } else {
4924
+ current = void 0;
4925
+ }
4744
4926
  }
4745
4927
  if (current === null || current === void 0) return "";
4746
4928
  if (typeof current === "object" && current.url) {
@@ -4810,7 +4992,8 @@ async function findExistingDocument(strapi, uid, targetDocId, docLocale) {
4810
4992
  }
4811
4993
  async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
4812
4994
  const attrs = ct.attributes || {};
4813
- const { mode = "auto", publish = true, user } = options;
4995
+ const { mode = "auto", publish = true, publishStrategy = "safe", user } = options;
4996
+ const isDraftAndPublish = Boolean(ct.options?.draftAndPublish);
4814
4997
  const stats = {
4815
4998
  total: rows.length,
4816
4999
  created: 0,
@@ -5025,7 +5208,9 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5025
5208
  };
5026
5209
  if (rowLocale) updateArgs.locale = rowLocale;
5027
5210
  await strapi.documents(uid).update(updateArgs);
5028
- if (publish && ct.options?.draftAndPublish) {
5211
+ const isApproved = isDraftAndPublish && Boolean(existing?.publishedAt) && existing?.status !== "modified";
5212
+ const shouldPublish = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : isApproved);
5213
+ if (shouldPublish) {
5029
5214
  try {
5030
5215
  const pubArgs = { documentId: resolvedDocId };
5031
5216
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5039,7 +5224,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5039
5224
  uid,
5040
5225
  documentId: resolvedDocId,
5041
5226
  userId: user?.id,
5042
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5227
+ statusOverride: shouldPublish ? "published" : isDraftAndPublish ? "draft" : void 0
5043
5228
  });
5044
5229
  } catch {
5045
5230
  }
@@ -5078,7 +5263,9 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5078
5263
  };
5079
5264
  if (rowLocale) updateArgs.locale = rowLocale;
5080
5265
  await strapi.documents(uid).update(updateArgs);
5081
- if (publish && ct.options?.draftAndPublish) {
5266
+ const isApproved = isDraftAndPublish && Boolean(existing?.publishedAt) && existing?.status !== "modified";
5267
+ const shouldPublish = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : isApproved);
5268
+ if (shouldPublish) {
5082
5269
  try {
5083
5270
  const pubArgs = { documentId: resolvedDocId };
5084
5271
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5092,7 +5279,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5092
5279
  uid,
5093
5280
  documentId: resolvedDocId,
5094
5281
  userId: user?.id,
5095
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5282
+ statusOverride: shouldPublish ? "published" : isDraftAndPublish ? "draft" : void 0
5096
5283
  });
5097
5284
  } catch {
5098
5285
  }
@@ -5118,7 +5305,8 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5118
5305
  if (rowLocale) createArgs.locale = rowLocale;
5119
5306
  const created = await strapi.documents(uid).create(createArgs);
5120
5307
  const newDocId = created?.documentId || docId;
5121
- if (publish && ct.options?.draftAndPublish && newDocId) {
5308
+ const shouldPublishNew = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : false);
5309
+ if (shouldPublishNew && newDocId) {
5122
5310
  try {
5123
5311
  const pubArgs = { documentId: newDocId };
5124
5312
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5132,7 +5320,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5132
5320
  uid,
5133
5321
  documentId: newDocId,
5134
5322
  userId: user?.id,
5135
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5323
+ statusOverride: shouldPublishNew ? "published" : isDraftAndPublish ? "draft" : void 0
5136
5324
  });
5137
5325
  } catch {
5138
5326
  }
@@ -5172,7 +5360,9 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5172
5360
  };
5173
5361
  if (rowLocale) updateArgs.locale = rowLocale;
5174
5362
  await strapi.documents(uid).update(updateArgs);
5175
- if (publish && ct.options?.draftAndPublish) {
5363
+ const isApproved = isDraftAndPublish && Boolean(existing?.publishedAt) && existing?.status !== "modified";
5364
+ const shouldPublish = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : isApproved);
5365
+ if (shouldPublish) {
5176
5366
  try {
5177
5367
  const pubArgs = { documentId: resolvedDocId };
5178
5368
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5186,7 +5376,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5186
5376
  uid,
5187
5377
  documentId: resolvedDocId,
5188
5378
  userId: user?.id,
5189
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5379
+ statusOverride: shouldPublish ? "published" : isDraftAndPublish ? "draft" : void 0
5190
5380
  });
5191
5381
  } catch {
5192
5382
  }
@@ -5212,7 +5402,8 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5212
5402
  if (rowLocale) createArgs.locale = rowLocale;
5213
5403
  const created = await strapi.documents(uid).create(createArgs);
5214
5404
  const newDocId = created?.documentId || docId;
5215
- if (publish && ct.options?.draftAndPublish && newDocId) {
5405
+ const shouldPublishNew = isDraftAndPublish && Boolean(publish) && (publishStrategy === "all" ? true : false);
5406
+ if (shouldPublishNew && newDocId) {
5216
5407
  try {
5217
5408
  const pubArgs = { documentId: newDocId };
5218
5409
  if (rowLocale) pubArgs.locale = rowLocale;
@@ -5226,7 +5417,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5226
5417
  uid,
5227
5418
  documentId: newDocId,
5228
5419
  userId: user?.id,
5229
- statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
5420
+ statusOverride: shouldPublishNew ? "published" : isDraftAndPublish ? "draft" : void 0
5230
5421
  });
5231
5422
  } catch {
5232
5423
  }
@@ -5258,7 +5449,7 @@ async function processCsvRows(strapi, ct, uid, rows, startRowNumber, options) {
5258
5449
  }
5259
5450
  return stats;
5260
5451
  }
5261
- async function recordCsvAuditLog(strapi, ct, uid, batchId, stats, user) {
5452
+ async function recordCsvAuditLog(strapi, ct, uid, batchId, stats, user, publishOnApproval, publishStrategy, unreviewedConsent) {
5262
5453
  if (stats.updated === 0 && stats.created === 0) return;
5263
5454
  try {
5264
5455
  const auditService2 = strapi.plugin("bulk-updater").service("audit");
@@ -5302,7 +5493,14 @@ async function recordCsvAuditLog(strapi, ct, uid, batchId, stats, user) {
5302
5493
  firstname: userName || "Admin"
5303
5494
  };
5304
5495
  if (typeof auditService2.recordBatch === "function") {
5305
- await auditService2.recordBatch(batchSummary, stats.diffs, userObj);
5496
+ await auditService2.recordBatch(
5497
+ batchSummary,
5498
+ stats.diffs,
5499
+ userObj,
5500
+ publishOnApproval,
5501
+ publishStrategy,
5502
+ unreviewedConsent
5503
+ );
5306
5504
  } else if (typeof auditService2.logOperation === "function") {
5307
5505
  await auditService2.logOperation({
5308
5506
  ...batchSummary,
@@ -5629,7 +5827,9 @@ const csvService = ({ strapi }) => ({
5629
5827
  skipped: 0,
5630
5828
  newWithoutDocId: 0,
5631
5829
  errors: [],
5632
- diffs: []
5830
+ diffs: [],
5831
+ publishStrategy: options.publishStrategy,
5832
+ unreviewedConsent: options.unreviewedConsent
5633
5833
  };
5634
5834
  csvImportSessions.set(batchId, session);
5635
5835
  }
@@ -5643,7 +5843,17 @@ const csvService = ({ strapi }) => ({
5643
5843
  session.errors.push(...chunkStats.errors);
5644
5844
  session.diffs.push(...chunkStats.diffs);
5645
5845
  if (options.isLastChunk) {
5646
- await recordCsvAuditLog(strapi, ct, uid, batchId, session, options.user);
5846
+ await recordCsvAuditLog(
5847
+ strapi,
5848
+ ct,
5849
+ uid,
5850
+ batchId,
5851
+ session,
5852
+ options.user,
5853
+ options.publish !== false,
5854
+ session.publishStrategy || options.publishStrategy,
5855
+ session.unreviewedConsent ?? options.unreviewedConsent
5856
+ );
5647
5857
  try {
5648
5858
  const bulkService = strapi.plugin("bulk-updater").service("bulkUpdater");
5649
5859
  if (bulkService && bulkService.invalidateCollectionCache) {
@@ -5697,7 +5907,17 @@ const csvService = ({ strapi }) => ({
5697
5907
  }
5698
5908
  } catch {
5699
5909
  }
5700
- await recordCsvAuditLog(strapi, ct, uid, batchId, stats, options.user);
5910
+ await recordCsvAuditLog(
5911
+ strapi,
5912
+ ct,
5913
+ uid,
5914
+ batchId,
5915
+ stats,
5916
+ options.user,
5917
+ options.publish !== false,
5918
+ options.publishStrategy,
5919
+ options.unreviewedConsent
5920
+ );
5701
5921
  return {
5702
5922
  success: true,
5703
5923
  batchId,
@@ -5717,7 +5937,17 @@ const csvService = ({ strapi }) => ({
5717
5937
  ...options,
5718
5938
  user: effectiveUser
5719
5939
  });
5720
- await recordCsvAuditLog(strapi, ct, uid, batchId, stats, effectiveUser);
5940
+ await recordCsvAuditLog(
5941
+ strapi,
5942
+ ct,
5943
+ uid,
5944
+ batchId,
5945
+ stats,
5946
+ effectiveUser,
5947
+ options.publish !== false,
5948
+ options.publishStrategy,
5949
+ options.unreviewedConsent
5950
+ );
5721
5951
  try {
5722
5952
  const bulkService = strapi.plugin("bulk-updater").service("bulkUpdater");
5723
5953
  if (bulkService && bulkService.invalidateCollectionCache) {
@@ -5755,6 +5985,15 @@ const csvService = ({ strapi }) => ({
5755
5985
  let validRows = 0;
5756
5986
  let estimatedUpdates = 0;
5757
5987
  let estimatedCreates = 0;
5988
+ const isDraftAndPublish = Boolean(ct.options?.draftAndPublish);
5989
+ const lifecycleStats = {
5990
+ isDraftAndPublish,
5991
+ totalChecked: 0,
5992
+ published: 0,
5993
+ modified: 0,
5994
+ draft: 0,
5995
+ unreviewedCount: 0
5996
+ };
5758
5997
  const errors = [];
5759
5998
  const warnings = [];
5760
5999
  for (let i = 0; i < rows.length; i++) {
@@ -5767,6 +6006,19 @@ const csvService = ({ strapi }) => ({
5767
6006
  if (docId) {
5768
6007
  existing = await findExistingDocument(strapi, uid, docId, rowLocale);
5769
6008
  }
6009
+ if (existing && isDraftAndPublish) {
6010
+ lifecycleStats.totalChecked++;
6011
+ const isApproved = Boolean(existing.publishedAt) && existing.status !== "modified";
6012
+ if (isApproved) {
6013
+ lifecycleStats.published++;
6014
+ } else if (existing.status === "modified") {
6015
+ lifecycleStats.modified++;
6016
+ lifecycleStats.unreviewedCount++;
6017
+ } else {
6018
+ lifecycleStats.draft++;
6019
+ lifecycleStats.unreviewedCount++;
6020
+ }
6021
+ }
5770
6022
  if (mode === "update") {
5771
6023
  if (!docId) {
5772
6024
  errors.push({
@@ -5885,6 +6137,7 @@ const csvService = ({ strapi }) => ({
5885
6137
  validRows,
5886
6138
  estimatedUpdates,
5887
6139
  estimatedCreates,
6140
+ lifecycleStats,
5888
6141
  hasErrors: errors.length > 0,
5889
6142
  hasWarnings: warnings.length > 0,
5890
6143
  errors: errors.slice(0, 100),
@@ -5910,6 +6163,9 @@ const csvService = ({ strapi }) => ({
5910
6163
  forceImmediate = false,
5911
6164
  mode = "auto",
5912
6165
  publish = true,
6166
+ publishStrategy = "safe",
6167
+ unreviewedConsent = false,
6168
+ unreviewedCount = 0,
5913
6169
  user
5914
6170
  } = options;
5915
6171
  const executionConfig = getExecutionWindowConfig(strapi);
@@ -5934,22 +6190,33 @@ const csvService = ({ strapi }) => ({
5934
6190
  options: {
5935
6191
  mode,
5936
6192
  publish,
6193
+ publishStrategy,
6194
+ unreviewedConsent,
5937
6195
  user
5938
6196
  },
5939
6197
  attribute: `CSV Import: ${collectionDisplayName}`,
5940
6198
  value: `${rows.length} rows (${mode} mode)`,
5941
- publishOnApproval: publish
6199
+ publishOnApproval: publish,
6200
+ publishStrategy,
6201
+ unreviewedConsent,
6202
+ unreviewedCount
5942
6203
  },
5943
6204
  summary: {
5944
6205
  attribute: `CSV Import: ${collectionDisplayName}`,
5945
6206
  value: `${rows.length} rows (${mode} mode)`,
5946
6207
  totalTargets: rows.length,
5947
6208
  collectionBreakdown: { [collectionDisplayName]: rows.length },
5948
- publishOnApproval: publish
6209
+ publishOnApproval: publish,
6210
+ publishStrategy,
6211
+ unreviewedConsent,
6212
+ unreviewedCount
5949
6213
  },
5950
6214
  status: "pending_approval",
5951
6215
  executionMode: "maker_checker",
5952
- makerNotes
6216
+ makerNotes,
6217
+ publishStrategy,
6218
+ unreviewedConsent,
6219
+ unreviewedCount
5953
6220
  };
5954
6221
  registerPendingBatch(batch, {
5955
6222
  id: batchId,
@@ -5966,6 +6233,9 @@ const csvService = ({ strapi }) => ({
5966
6233
  failedCount: 0,
5967
6234
  isLive: false,
5968
6235
  publishOnApproval: publish,
6236
+ publishStrategy,
6237
+ unreviewedConsent,
6238
+ unreviewedCount,
5969
6239
  makerNotes,
5970
6240
  results: []
5971
6241
  });
@@ -5997,23 +6267,34 @@ const csvService = ({ strapi }) => ({
5997
6267
  options: {
5998
6268
  mode,
5999
6269
  publish,
6270
+ publishStrategy,
6271
+ unreviewedConsent,
6000
6272
  user
6001
6273
  },
6002
6274
  attribute: `CSV Import: ${collectionDisplayName}`,
6003
6275
  value: `${rows.length} rows (${mode} mode)`,
6004
- publishOnApproval: publish
6276
+ publishOnApproval: publish,
6277
+ publishStrategy,
6278
+ unreviewedConsent,
6279
+ unreviewedCount
6005
6280
  },
6006
6281
  summary: {
6007
6282
  attribute: `CSV Import: ${collectionDisplayName}`,
6008
6283
  value: `${rows.length} rows (${mode} mode)`,
6009
6284
  totalTargets: rows.length,
6010
6285
  collectionBreakdown: { [collectionDisplayName]: rows.length },
6011
- publishOnApproval: publish
6286
+ publishOnApproval: publish,
6287
+ publishStrategy,
6288
+ unreviewedConsent,
6289
+ unreviewedCount
6012
6290
  },
6013
6291
  status: "queued_offpeak",
6014
6292
  executionMode: "queue_offpeak",
6015
6293
  scheduledFor: executionConfig.offPeakStartTime,
6016
- makerNotes
6294
+ makerNotes,
6295
+ publishStrategy,
6296
+ unreviewedConsent,
6297
+ unreviewedCount
6017
6298
  };
6018
6299
  registerPendingBatch(batch, {
6019
6300
  id: batchId,
@@ -6031,6 +6312,9 @@ const csvService = ({ strapi }) => ({
6031
6312
  failedCount: 0,
6032
6313
  isLive: false,
6033
6314
  publishOnApproval: publish,
6315
+ publishStrategy,
6316
+ unreviewedConsent,
6317
+ unreviewedCount,
6034
6318
  makerNotes,
6035
6319
  results: []
6036
6320
  });
@@ -6063,23 +6347,34 @@ const csvService = ({ strapi }) => ({
6063
6347
  options: {
6064
6348
  mode,
6065
6349
  publish,
6350
+ publishStrategy,
6351
+ unreviewedConsent,
6066
6352
  user
6067
6353
  },
6068
6354
  attribute: `CSV Import: ${collectionDisplayName}`,
6069
6355
  value: `${rows.length} rows (${mode} mode)`,
6070
- publishOnApproval: publish
6356
+ publishOnApproval: publish,
6357
+ publishStrategy,
6358
+ unreviewedConsent,
6359
+ unreviewedCount
6071
6360
  },
6072
6361
  summary: {
6073
6362
  attribute: `CSV Import: ${collectionDisplayName}`,
6074
6363
  value: `${rows.length} rows (${mode} mode)`,
6075
6364
  totalTargets: rows.length,
6076
6365
  collectionBreakdown: { [collectionDisplayName]: rows.length },
6077
- publishOnApproval: publish
6366
+ publishOnApproval: publish,
6367
+ publishStrategy,
6368
+ unreviewedConsent,
6369
+ unreviewedCount
6078
6370
  },
6079
6371
  status: "queued_offpeak",
6080
6372
  executionMode: "queue_offpeak",
6081
6373
  scheduledFor: executionConfig.offPeakStartTime,
6082
- makerNotes: "Automatically queued due to peak business hours restriction."
6374
+ makerNotes: "Automatically queued due to peak business hours restriction.",
6375
+ publishStrategy,
6376
+ unreviewedConsent,
6377
+ unreviewedCount
6083
6378
  };
6084
6379
  registerPendingBatch(batch, {
6085
6380
  id: batchId,
@@ -6097,6 +6392,9 @@ const csvService = ({ strapi }) => ({
6097
6392
  failedCount: 0,
6098
6393
  isLive: false,
6099
6394
  publishOnApproval: publish,
6395
+ publishStrategy,
6396
+ unreviewedConsent,
6397
+ unreviewedCount,
6100
6398
  policyMessage,
6101
6399
  makerNotes: "Automatically queued due to peak business hours restriction.",
6102
6400
  results: []