true], 'objects');
unset($types['attachment']);
uasort($types, static function ($left, $right): int {
return strcasecmp((string) $left->label, (string) $right->label);
});
return $types;
}
function samtyk_cmp_search_public_content(string $post_type, string $search): array
{
$types = samtyk_cmp_get_public_content_types();
if (!isset($types[$post_type])) {
return [];
}
$posts = get_posts([
'post_type' => $post_type,
'post_status' => 'publish',
'posts_per_page' => 20,
's' => trim($search),
'orderby' => 'title',
'order' => 'ASC',
'no_found_rows' => true,
]);
return array_map(static function ($post) use ($types): array {
return [
'id' => (int) $post->ID,
'title' => (string) $post->post_title,
'post_type' => (string) $post->post_type,
'type_label' => (string) $types[$post->post_type]->label,
];
}, array_slice($posts, 0, 20));
}
function samtyk_cmp_get_snippet_request_input(array $request): array
{
$source = isset($request['samtyk_cmp_snippet_source']) && is_string($request['samtyk_cmp_snippet_source'])
? wp_unslash($request['samtyk_cmp_snippet_source'])
: '';
$source_error = '';
if (array_key_exists('samtyk_cmp_snippet_source_base64', $request)) {
$encoded = $request['samtyk_cmp_snippet_source_base64'];
$source = '';
if (!is_string($encoded)) {
$source_error = __('The code could not be decoded. Reload the page and try again.', 'samtyk-cmp');
} elseif (strlen($encoded) > 4 * (int) ceil(SAMTYK_CMP_SNIPPET_MAX_BYTES / 3)) {
$source_error = __('The code is larger than the allowed 100 KB.', 'samtyk-cmp');
} else {
$encoded = wp_unslash($encoded);
$decoded = base64_decode($encoded, true);
if ($decoded === false || base64_encode($decoded) !== $encoded) {
$source_error = __('The code could not be decoded. Reload the page and try again.', 'samtyk-cmp');
} elseif (strlen($decoded) > SAMTYK_CMP_SNIPPET_MAX_BYTES) {
$source_error = __('The code is larger than the allowed 100 KB.', 'samtyk-cmp');
} else {
// Decoded source is already unslashed; keep JavaScript backslashes intact.
$source = $decoded;
}
}
}
$categories = $request['samtyk_cmp_categories'] ?? [];
// Preserve an invalid marker instead of silently converting malformed consent to none.
$categories = samtyk_cmp_valid_snippet_categories($categories) ? $categories : ['invalid'];
foreach ($request as $key => $value) {
if (!in_array($key, ['samtyk_cmp_categories', 'samtyk_cmp_content_ids'], true) && !is_string($value)) {
$request[$key] = '';
}
}
$content_ids = $request['samtyk_cmp_content_ids'] ?? [];
return [
'name' => isset($request['samtyk_cmp_snippet_name']) ? sanitize_text_field(wp_unslash($request['samtyk_cmp_snippet_name'])) : '',
'source' => $source,
'source_error' => $source_error,
'categories' => $categories,
'location' => isset($request['samtyk_cmp_location']) ? sanitize_key(wp_unslash($request['samtyk_cmp_location'])) : '',
'shortcode_id' => isset($request['samtyk_cmp_shortcode_id']) ? sanitize_key(wp_unslash($request['samtyk_cmp_shortcode_id'])) : '',
'placeholder_enabled' => !empty($request['samtyk_cmp_placeholder_enabled']),
'placeholder_default' => !empty($request['samtyk_cmp_placeholder_default']),
'placeholder_text' => isset($request['samtyk_cmp_placeholder_text']) && is_string($request['samtyk_cmp_placeholder_text'])
? sanitize_textarea_field(wp_unslash($request['samtyk_cmp_placeholder_text'])) : '',
'placeholder_icon' => isset($request['samtyk_cmp_placeholder_icon']) ? sanitize_key(wp_unslash($request['samtyk_cmp_placeholder_icon'])) : '',
'active' => !empty($request['samtyk_cmp_active']),
'visibility' => isset($request['samtyk_cmp_visibility']) ? sanitize_key(wp_unslash($request['samtyk_cmp_visibility'])) : 'all',
'content_ids' => is_array($content_ids) ? array_map('intval', wp_unslash($content_ids)) : [],
];
}
function samtyk_cmp_snippets_admin_url(array $args = []): string
{
return add_query_arg(array_merge([
'page' => SAMTYK_CMP_MENU_SLUG,
'view' => 'scripts',
], $args), admin_url('admin.php'));
}
function samtyk_cmp_toggle_snippet(int $post_id): bool
{
if (!current_user_can('manage_options')) {
return false;
}
$snippet = samtyk_cmp_get_snippet($post_id);
if (!$snippet) {
return false;
}
if (!$snippet['active'] && (!samtyk_cmp_can_manage_snippet_code() || !$snippet['approved'] || $snippet['generated_code'] === '')) {
return false;
}
$result = wp_update_post([
'ID' => $post_id,
'post_status' => $snippet['active'] ? 'draft' : 'publish',
], true);
return !is_wp_error($result) && (int) $result > 0;
}
function samtyk_cmp_delete_snippet(int $post_id): bool
{
if (!current_user_can('manage_options')) {
return false;
}
if (!samtyk_cmp_get_snippet($post_id)) {
return false;
}
return (bool) wp_delete_post($post_id, true);
}
function samtyk_cmp_handle_snippet_admin_action(): void
{
global $samtyk_cmp_snippet_form_state;
if (wp_doing_ajax()) {
return;
}
// phpcs:disable WordPress.Security.NonceVerification.Missing -- This request is ignored unless it belongs to this handler, then the nonce is verified by samtyk_cmp_require_snippet_admin_access() before any state change.
if (empty($_POST['samtyk_cmp_snippet_action'])) {
return;
}
$action = is_string($_POST['samtyk_cmp_snippet_action']) ? sanitize_key(wp_unslash($_POST['samtyk_cmp_snippet_action'])) : '';
if (!in_array($action, ['save', 'toggle', 'delete'], true)) {
return;
}
$nonce = isset($_POST['samtyk_cmp_snippet_nonce']) && is_string($_POST['samtyk_cmp_snippet_nonce'])
? sanitize_text_field(wp_unslash($_POST['samtyk_cmp_snippet_nonce']))
: '';
samtyk_cmp_require_snippet_admin_access($nonce, 'samtyk_cmp_manage_snippets');
$post_id = isset($_POST['samtyk_cmp_snippet_id']) ? (int) $_POST['samtyk_cmp_snippet_id'] : 0;
if ($action === 'save') {
$input = samtyk_cmp_get_snippet_request_input($_POST);
$result = samtyk_cmp_save_snippet($input, $post_id);
if (!$result['success']) {
$samtyk_cmp_snippet_form_state = [
'input' => $input,
'post_id' => $post_id,
'error' => $result['error'],
];
return;
}
wp_safe_redirect(samtyk_cmp_snippets_admin_url(['updated' => '1']));
exit;
}
$snippet = samtyk_cmp_get_snippet($post_id);
if (!$snippet) {
wp_safe_redirect(samtyk_cmp_snippets_admin_url(['snippet_error' => 'not_found']));
exit;
}
if ($action === 'delete') {
samtyk_cmp_delete_snippet($post_id);
wp_safe_redirect(samtyk_cmp_snippets_admin_url(['deleted' => '1']));
exit;
}
if (!samtyk_cmp_toggle_snippet($post_id)) {
$samtyk_cmp_snippet_form_state = [
'input' => [], 'post_id' => $post_id,
'error' => __('Review and save this snippet with the unfiltered_html capability before activating it.', 'samtyk-cmp'),
];
return;
}
wp_safe_redirect(samtyk_cmp_snippets_admin_url(['toggled' => '1']));
// phpcs:enable WordPress.Security.NonceVerification.Missing
exit;
}
function samtyk_cmp_ajax_preview_snippet(): void
{
// phpcs:disable WordPress.Security.NonceVerification.Missing -- The nonce is verified by samtyk_cmp_require_snippet_admin_access() before request data is processed.
$nonce = isset($_POST['nonce']) && is_string($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
samtyk_cmp_require_snippet_admin_access($nonce, 'samtyk_cmp_preview_snippet');
$input = samtyk_cmp_get_snippet_request_input($_POST);
// phpcs:enable WordPress.Security.NonceVerification.Missing
if ($input['source_error'] !== '') {
wp_send_json_error(['message' => $input['source_error']], 422);
}
$result = samtyk_cmp_convert_embed_snippet($input['source'], $input['categories'], '');
if (!$result['success']) {
wp_send_json_error(['message' => $result['error']], 422);
}
wp_send_json_success(['html' => $result['html']]);
}
function samtyk_cmp_ajax_search_public_content(): void
{
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- The nonce is verified by samtyk_cmp_require_snippet_admin_access() before request data is processed.
$nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : '';
samtyk_cmp_require_snippet_admin_access($nonce, 'samtyk_cmp_search_content');
$post_type = isset($_GET['post_type']) ? sanitize_key(wp_unslash($_GET['post_type'])) : '';
$search = isset($_GET['search']) ? sanitize_text_field(wp_unslash($_GET['search'])) : '';
// phpcs:enable WordPress.Security.NonceVerification.Recommended
wp_send_json_success(['items' => samtyk_cmp_search_public_content($post_type, $search)]);
}
function samtyk_cmp_get_snippet_form_value(?array $snippet, array $input, string $key, $default = null)
{
if (array_key_exists($key, $input)) {
return $input[$key];
}
if ($snippet !== null && array_key_exists($key, $snippet)) {
return $snippet[$key];
}
return $default;
}
function samtyk_cmp_get_snippet_list_page(int $page = 1, int $per_page = 20): array
{
$page = max(1, $page);
$per_page = max(1, min(100, $per_page));
$counts = wp_count_posts(SAMTYK_CMP_SNIPPET_POST_TYPE);
$total = (int) ($counts->publish ?? 0) + (int) ($counts->draft ?? 0);
$pages = max(1, (int) ceil($total / $per_page));
$page = min($page, $pages);
$posts = get_posts([
'post_type' => SAMTYK_CMP_SNIPPET_POST_TYPE,
'post_status' => ['publish', 'draft'],
'posts_per_page' => $per_page,
'offset' => ($page - 1) * $per_page,
// Keep rows stable when editing or toggling status, including across paginated results.
'orderby' => 'ID',
'order' => 'DESC',
]);
return [
'posts' => $posts,
'page' => $page,
'pages' => $pages,
'total' => $total,
];
}
function samtyk_cmp_get_action_icon(string $icon): string
{
$paths = [
'edit' => ' ' . esc_html(__('Convert code, review it, and optionally insert it on the website.', 'samtyk-cmp')) . ' ' . esc_html__('This snippet is paused. Review the code and save it again to approve frontend output.', 'samtyk-cmp') . ' ' . esc_html__('Managing executable code requires the unfiltered_html capability.', 'samtyk-cmp') . '' . esc_html($post_id > 0 ? __('Edit script or embed', 'samtyk-cmp') : __('Add script or embed', 'samtyk-cmp')) . '
' . esc_html(__('Convert, insert, and manage consent-controlled code.', 'samtyk-cmp')) . '
' . esc_html(__('No scripts or embeds have been added yet.', 'samtyk-cmp')) . '
| ' . esc_html(__('Name', 'samtyk-cmp')) . ' | ' . esc_html(__('Categories', 'samtyk-cmp')) . ' | ' . esc_html(__('Placement', 'samtyk-cmp')) . ' | ' . esc_html(__('Show on', 'samtyk-cmp')) . ' | ' . esc_html(__('Modified', 'samtyk-cmp')) . ' | ' . esc_html(__('Actions', 'samtyk-cmp')) . ' |
|---|---|---|---|---|---|
| ' . esc_html($snippet['name']) . ''; if ($status_label !== '') { echo '' . esc_html($status_label) . ''; } echo ' | ' . esc_html(implode(', ', $category_labels)) . ' | ' . esc_html($location_label) . ' | ' . esc_html($visibility_label) . ' | ' . esc_html($snippet['modified']) . ' | ' . wp_kses(samtyk_cmp_get_action_icon('edit'), samtyk_cmp_get_action_icon_allowed_html()) . '' . esc_html($edit_label) . '';
foreach (['toggle' => ($snippet['active'] ? __('Deactivate', 'samtyk-cmp') : __('Activate', 'samtyk-cmp')), 'delete' => __('Delete', 'samtyk-cmp')] as $action => $label) {
$icon = $action === 'delete' ? 'delete' : ($snippet['active'] ? 'deactivate' : 'activate');
echo '';
}
echo ' |