isUsable()) { return 0; } $items = (array) ($template->payload['items'] ?? []); if ($items === []) { return 0; } $db = $this->db(); $userId = (int) ($options['userId'] ?? 0); $mode = $options['mode'] ?? 'add'; if ($mode === 'replace') { $db->table('zp_canvas_items') ->where('canvasId', $targetId) ->delete(); } $sortBase = $this->nextSortIndex($targetId); $created = 0; // One timestamp for the whole apply — all items from the same template // application should share created/modified so recent-activity sorts // don't rank them arbitrarily against each other. $now = now(); foreach ($items as $offset => $item) { if (! is_array($item) || empty($item['box'])) { continue; } $row = [ 'canvasId' => $targetId, 'box' => (string) $item['box'], 'description' => (string) ($item['title'] ?? ''), 'conclusion' => (string) ($item['description'] ?? ''), 'status' => (string) ($item['status'] ?? ''), 'author' => $userId, 'created' => $now, // MAX(zp_canvas_items.modified) is the source of truth for a // board's "last updated" timestamp (see BlueprintsRepository's // COALESCE(MAX(modified), created) sort). MAX() ignores nulls, // so leaving modified null makes templated boards appear stale // in recent-activity lists even though items were just added. 'modified' => $now, 'sortindex' => (int) ($item['sortindex'] ?? ($sortBase + $offset * 10)), ]; // Authored-meaning fields pass through if present in the template. // YAML keys match DB column names — no remapping like title→description. if (array_key_exists('why_this_matters', $item)) { $row['why_this_matters'] = (string) $item['why_this_matters']; } if (array_key_exists('starting_picture', $item)) { $row['starting_picture'] = (string) $item['starting_picture']; } $db->table('zp_canvas_items')->insert($row); $created++; } return $created; } /** * Find the next free sortindex on this canvas, leaving room between entries. * Returns at least 10 so the first item is sortindex >= 10. */ private function nextSortIndex(int $canvasId): int { $max = (int) $this->db()->table('zp_canvas_items') ->where('canvasId', $canvasId) ->max('sortindex'); return $max > 0 ? $max + 10 : 10; } private function db(): ConnectionInterface { return $this->db ??= $this->dbCore->getConnection(); } }