*/ protected function document(Session $session): array { $state = StudioState::fold(Transcript::visible($this->store->entries($session->id))); $fresh = $this->sessions->find($this->userId(), $session->uuid) ?? $session; $saved = $this->handoff->state($fresh); if ($saved !== null) { $state['post_type'] = $saved['post']->post_type; } return $this->stageView($state, $saved !== null) + [ 'session' => SessionResource::make($fresh), 'template' => $state['template'], 'post_type' => $state['post_type'], 'document' => $state['working'], 'saved' => $saved === null ? null : StudioSavedResource::make($saved), 'rev' => $state['rev'], 'outline' => $state['outline'] === null ? null : $state['outline'] + ['written' => array_keys($state['written'])], 'proposal' => $state['proposal'], 'words' => $state['working'] === null ? 0 : StudioState::words($state['working']['body']), ]; } /** * Where the document is in its stages, for the stage rail, and the instructions each stage uses now. * * @param State $state * @return array */ protected function stageView(array $state, bool $handedOff): array { $current = StudioState::current($state, $handedOff); $order = $state['stages']; $stages = []; foreach (Stages::all() as $spec) { if (!in_array($spec['id'], $order, true)) { continue; } $mark = $state['progress'][$spec['id']] ?? ''; $before = array_search($spec['id'], $order, true) < array_search($current, $order, true); $stages[] = [ 'id' => $spec['id'], 'label' => $spec['label'], 'description' => $spec['description'], 'kind' => $spec['kind'], 'required' => $spec['required'], 'status' => $spec['id'] === $current ? 'current' : ($mark === 'skipped' ? 'skipped' : ($before || $mark === 'done' ? 'done' : 'todo')), ]; } $instructions = []; foreach (Stages::withSlots() as $stage) { if (in_array($stage, $order, true)) { $instructions[$stage] = InstructionSet::effective($stage, $state['instructions'][$stage] ?? []); } } return [ 'stages' => $stages, 'current' => $current, 'brief' => $state['brief'], 'notes' => $state['notes'], 'instructions' => (object) $instructions, 'fields' => (object) $state['fields'], ]; } /** * The folded state of a document. * * @return State */ protected function state(Session $session): array { return StudioState::fold(Transcript::visible($this->store->entries($session->id))); } /** * The edited document from the request, or why it cannot be used. * * @return array{title: string, excerpt: string, body: string}|string */ protected function input(Request $request): array|string { $document = [ 'title' => sanitize_text_field((string) $request->input('title', '')), 'excerpt' => sanitize_textarea_field((string) $request->input('excerpt', '')), 'body' => Redactor::cleanText((string) $request->input('body', '')), ]; if (mb_strlen($document['title']) > 200 || mb_strlen($document['excerpt']) > 1000) { return __('The title can have 200 characters and the excerpt 1,000.', 'maze-graph'); } if (mb_strlen($document['body']) > self::MAX_BODY) { /* translators: %s: maximum number of characters. */ return sprintf(__('The text can have at most %s characters.', 'maze-graph'), number_format_i18n(self::MAX_BODY)); } return $document; } protected function postType(Session $session): string { $saved = $this->handoff->state($session); if ($saved !== null) { return $saved['post']->post_type; } return StudioState::fold(Transcript::visible($this->store->entries($session->id)))['post_type']; } protected function clientId(Request $request): string { return substr((string) preg_replace('/[^A-Za-z0-9-]/', '', (string) $request->input('message_id', '')), 0, 64); } protected function on(): bool { return $this->extensions->modules()->enabled(self::MODULE); } protected function off(): JsonResponse { return $this->error(__('Content Studio is switched off. An administrator can switch it on in Tools → Extensions.', 'maze-graph'), 503); } }