Requests::apply($id), 'decline' => Requests::decline($id, $note), 'withdraw' => Requests::withdraw($id), default => ['error' => __('Unknown action.', 'sarcio'), 'ok' => false], }; if (!$result['ok']) { set_transient('sarcio_request_error_' . get_current_user_id(), (string) ($result['error'] ?? __('Did not work', 'sarcio')), 120); } wp_safe_redirect(add_query_arg('sarcio-request', $result['ok'] ? $decision : 'failed', self::url($id))); exit; } public static function url(string $id = ''): string { return admin_url('admin.php?page=' . self::SLUG . ($id !== '' ? '&request=' . rawurlencode($id) : '')); } public static function badge(): string { if (!current_user_can(Requests::CAPABILITY)) { return ''; } $count = Requests::pendingCount(); return $count > 0 ? ' ' . (int) $count . '' : ''; } public function enqueue(string $hook): void { if (!str_ends_with($hook, '_page_' . self::SLUG)) { return; } wp_add_inline_style('common', ' .sarcio-requests { max-width: 860px; } .sarcio-request { background: #fff; border: 1px solid #dcdcde; border-radius: 8px; padding: 1rem 1.25rem; margin: 0 0 1rem; } .sarcio-request.is-target { border-color: #2271b1; box-shadow: 0 0 0 2px rgba(34,113,177,.2); } .sarcio-request-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1rem; } .sarcio-request-head h2 { margin: 0; font-size: 16px; } .sarcio-request-meta { color: #50575e; font-size: 13px; } .sarcio-request-status { font-size: 12px; font-weight: 600; padding: .1rem .5rem; border-radius: 999px; background: #f0f0f1; } .sarcio-request-status.is-pending { background: #fcf0d6; color: #7a4a00; } .sarcio-request-status.is-applied { background: #e2f5ea; color: #0b6b3a; } .sarcio-request-status.is-declined, .sarcio-request-status.is-failed { background: #fbeaea; color: #8a1f1f; } .sarcio-request-change { margin: .75rem 0 0; } .sarcio-request-change strong { display: block; margin: 0 0 .25rem; } .sarcio-request-diff { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; } .sarcio-request-diff pre { margin: 0; padding: .5rem .6rem; white-space: pre-wrap; word-break: break-word; font: 12px/1.5 ui-monospace, Menlo, monospace; border-radius: 6px; max-height: 240px; overflow: auto; } .sarcio-request-diff .is-before { background: #fbeaea; } .sarcio-request-diff .is-after { background: #e2f5ea; } .sarcio-request-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: 1rem 0 0; } .sarcio-request-actions textarea { width: 100%; max-width: 480px; } .sarcio-request-note { margin: .75rem 0 0; color: #50575e; } .sarcio-request-error { color: #8a1f1f; } .sarcio-request-agency { margin: .5rem 0 0; padding: .5rem .75rem; border-left: 4px solid #dba617; background: #fcf9e8; } @media (max-width: 782px) { .sarcio-request-diff { grid-template-columns: 1fr; } } '); } public function render(): void { if (!current_user_can(ReviewPage::CAPABILITY)) { return; } $target = isset($_GET['request']) ? sanitize_text_field(wp_unslash((string) $_GET['request'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- which request to highlight; nothing is written. $canApply = current_user_can(Requests::CAPABILITY); $requests = Requests::list(); $outcome = isset($_GET['sarcio-request']) ? sanitize_key(wp_unslash((string) $_GET['sarcio-request'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- a notice after the redirect; nothing is written. $notices = [ 'apply' => __('Applied. The change is live on the site.', 'sarcio'), 'decline' => __('Declined. The person who asked has been told.', 'sarcio'), 'withdraw' => __('Withdrawn.', 'sarcio'), ]; echo '
'; echo '

' . esc_html(Brand::screen(__('Requests', 'sarcio'))) . '

'; if ($outcome === 'failed') { $error = (string) get_transient('sarcio_request_error_' . get_current_user_id()); delete_transient('sarcio_request_error_' . get_current_user_id()); echo '

' . esc_html($error !== '' ? $error : __('Did not work', 'sarcio')) . '

'; } elseif (isset($notices[$outcome])) { echo '

' . esc_html($notices[$outcome]) . '

'; } if (Bulk::enabled() && $canApply) { echo '

' . esc_html__('Changes your agency writes for several sites at once (Across sites) wait here too. Each one is only a preview until you press Apply, which makes it on this site under your name. They can change content, menus, media, products, comments, categories, SEO and the site’s look, never plugins, themes, users or settings.', 'sarcio') . '

'; } if (!Requests::approvalRequired()) { echo '

' . esc_html__('Everyone applies their own changes on this site. To have administrators approve changes instead, turn on requests under Settings → Sarcio → Assistant.', 'sarcio') . '

'; } elseif ($canApply) { echo '

' . esc_html__('Changes other members asked the assistant for. Each is shown as their preview showed it; Apply makes it as it is, under your name.', 'sarcio') . '

'; } else { echo '

' . esc_html__('What you sent to your agency, and what became of it.', 'sarcio') . '

'; } if ($requests === []) { echo '

' . esc_html__('No requests yet.', 'sarcio') . '

'; return; } foreach ($requests as $request) { self::card($request, $canApply, $request['id'] === $target); } echo ''; } /** @param array $request */ private static function card(array $request, bool $canApply, bool $isTarget): void { $status = (string) $request['status']; $labels = [ 'applied' => __('Applied', 'sarcio'), 'applying' => __('Being applied', 'sarcio'), 'declined' => __('Declined', 'sarcio'), 'failed' => __('Could not be applied', 'sarcio'), 'partial' => __('Partly applied', 'sarcio'), 'pending' => __('Waiting', 'sarcio'), 'withdrawn' => __('Withdrawn', 'sarcio'), ]; echo '
'; echo '

' . esc_html((string) $request['title']) . '

'; echo '' . esc_html($labels[$status] ?? $status) . ''; echo '' . esc_html(sprintf( /* translators: 1: who asked, 2: when. */ __('%1$s · %2$s', 'sarcio'), (string) $request['requestedBy'], substr((string) $request['createdAt'], 0, 16), )) . '
'; if (($request['origin'] ?? '') === Requests::ORIGIN_AGENCY && $status === 'pending') { echo '

' . esc_html__('Written by your agency in Sarcio, not by someone on this site. Check each change below: nothing happens until you press Apply, and Apply makes these changes under your name.', 'sarcio') . '

'; } foreach ($request['changes'] as $index => $change) { echo '
' . esc_html($change['summary'] !== '' ? (string) $change['summary'] : (string) $change['label']) . ''; if ($change['before'] !== '' || $change['after'] !== '') { echo '
' . esc_html((string) $change['before']) . '
' . esc_html((string) $change['after']) . '
'; } if ($index === array_key_last($request['changes']) && ($request['notes'] ?? []) !== []) { echo '

' . esc_html__('Notes pinned on the preview', 'sarcio') . '

    '; foreach ($request['notes'] as $note) { printf('
  • %s %s %s
  • ', esc_html((string) ($note['author'] !== '' ? $note['author'] : __('Someone', 'sarcio'))), esc_html((string) $note['text']), esc_html((string) $note['page'])); } echo '
'; } echo '
'; } if ((string) $request['note'] !== '') { echo '

' . esc_html((string) $request['note']) . '

'; } if ($status === 'pending') { echo '
'; echo ''; echo ''; wp_nonce_field(self::DECIDE_ACTION); if ($canApply) { echo ''; echo ''; echo ''; } if ($request['mine']) { echo ''; } echo '
'; } echo '
'; } }