[ 'name' => __('Case Study Categories', 'siteaudithub-case-studies'), 'singular_name' => __('Category', 'siteaudithub-case-studies'), 'menu_name' => __('Categories', 'siteaudithub-case-studies'), 'all_items' => __('All Categories', 'siteaudithub-case-studies'), 'edit_item' => __('Edit Category', 'siteaudithub-case-studies'), 'add_new_item' => __('Add New Category', 'siteaudithub-case-studies'), 'search_items' => __('Search Categories', 'siteaudithub-case-studies'), 'not_found' => __('No categories found.', 'siteaudithub-case-studies'), ], 'public' => true, 'hierarchical' => true, 'show_in_rest' => true, 'show_admin_column' => true, 'rewrite' => ['slug' => $s['archive_slug'] . '/' . $s['category_base'], 'with_front' => false], ]); register_post_type(SAHCS_CPT, [ 'labels' => [ 'name' => __('Case Studies', 'siteaudithub-case-studies'), 'singular_name' => __('Case Study', 'siteaudithub-case-studies'), 'menu_name' => __('Case Studies', 'siteaudithub-case-studies'), 'add_new' => __('Add New', 'siteaudithub-case-studies'), 'add_new_item' => __('Add New Case Study', 'siteaudithub-case-studies'), 'edit_item' => __('Edit Case Study', 'siteaudithub-case-studies'), 'new_item' => __('New Case Study', 'siteaudithub-case-studies'), 'view_item' => __('View Case Study', 'siteaudithub-case-studies'), 'view_items' => __('View Case Studies', 'siteaudithub-case-studies'), 'search_items' => __('Search Case Studies', 'siteaudithub-case-studies'), 'not_found' => __('No case studies found.', 'siteaudithub-case-studies'), 'not_found_in_trash' => __('No case studies found in Trash.', 'siteaudithub-case-studies'), 'all_items' => __('All Case Studies', 'siteaudithub-case-studies'), 'archives' => __('Case Study Archives', 'siteaudithub-case-studies'), 'featured_image' => __('Case study image', 'siteaudithub-case-studies'), 'set_featured_image' => __('Set case study image', 'siteaudithub-case-studies'), 'item_published' => __('Case study published.', 'siteaudithub-case-studies'), 'item_updated' => __('Case study updated.', 'siteaudithub-case-studies'), ], 'public' => true, 'show_in_rest' => true, 'menu_icon' => 'dashicons-portfolio', 'menu_position' => 21, 'has_archive' => $s['archive_slug'], 'rewrite' => ['slug' => $s['archive_slug'], 'with_front' => false], 'supports' => ['title', 'editor', 'excerpt', 'thumbnail', 'revisions', 'custom-fields', 'author'], 'template' => sahcs_default_block_template(), ]); // Per-post "show table of contents" switch (default / on / off). register_post_meta(SAHCS_CPT, '_sahcs_toc', [ 'type' => 'string', 'single' => true, 'default' => 'default', 'show_in_rest' => true, 'sanitize_callback' => function ($v) { return in_array($v, ['default', 'on', 'off'], true) ? $v : 'default'; }, 'auth_callback' => function () { return current_user_can('edit_posts'); }, ]); } /** Is "no base in the URL" active and usable? It needs /%postname%/ permalinks. */ function sahcs_root_mode() { return sahcs_get_settings('url_mode') === 'root' && sahcs_permalinks_support_root(); } function sahcs_permalinks_support_root() { return trim((string) get_option('permalink_structure'), '/') === '%postname%'; } /* ------------------------------------------------------------------ * Root mode: /my-case-study/ instead of /case-studies/my-case-study/ * ---------------------------------------------------------------- */ add_filter('post_type_link', function ($link, $post) { if ($post->post_type !== SAHCS_CPT || !sahcs_root_mode() || !$post->post_name) { return $link; } if (in_array($post->post_status, ['draft', 'pending', 'auto-draft', 'future'], true)) { return $link; // unpublished posts keep WordPress's preview links } return home_url(user_trailingslashit($post->post_name)); }, 10, 2); /** * WordPress sees /my-case-study/ as a normal post. If no post or page has * that slug but a case study does, point the request at the case study. */ add_filter('request', function ($vars) { if (is_admin() || !sahcs_root_mode()) { return $vars; } $slug = ''; if (!empty($vars['name']) && empty($vars['post_type']) && empty($vars['category_name'])) { $slug = $vars['name']; } elseif (!empty($vars['pagename']) && strpos($vars['pagename'], '/') === false) { $slug = $vars['pagename']; } if ($slug === '') { return $vars; } if (sahcs_slug_taken_by_post_or_page($slug)) { return $vars; } $case = get_page_by_path($slug, OBJECT, SAHCS_CPT); if (!$case) { return $vars; } $new = [ 'post_type' => SAHCS_CPT, 'name' => $slug, SAHCS_CPT => $slug, ]; if (!empty($vars['page'])) { $new['page'] = $vars['page']; } return $new; }); function sahcs_slug_taken_by_post_or_page($slug, $exclude_id = 0) { $ids = get_posts([ 'name' => $slug, 'post_type' => ['post', 'page'], 'post_status' => ['publish', 'future', 'private', 'draft', 'pending'], 'numberposts' => 2, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ]); return (bool) array_diff(array_map('intval', $ids), [(int) $exclude_id]); } function sahcs_slug_taken_by_case($slug, $exclude_id = 0) { $ids = get_posts([ 'name' => $slug, 'post_type' => SAHCS_CPT, 'post_status' => ['publish', 'future', 'private', 'draft', 'pending'], 'numberposts' => 2, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ]); return (bool) array_diff(array_map('intval', $ids), [(int) $exclude_id]); } /** * Keeps slugs unique between case studies, posts and top-level pages * while root mode is on, by adding -2, -3 ... like WordPress does. */ add_filter('wp_unique_post_slug', function ($slug, $post_id, $status, $type, $parent) { if (!sahcs_root_mode() || in_array($status, ['auto-draft', 'inherit'], true)) { return $slug; } if ($type === SAHCS_CPT) { $check = function ($candidate) use ($post_id) { return sahcs_slug_taken_by_post_or_page($candidate, $post_id); }; } elseif ($type === 'post' || ($type === 'page' && !$parent)) { $check = function ($candidate) use ($post_id) { return sahcs_slug_taken_by_case($candidate, $post_id); }; } else { return $slug; } if (!$check($slug)) { return $slug; } $base = preg_replace('/-\d+$/', '', $slug); $n = 2; do { $candidate = $base . '-' . $n; $n++; } while ($check($candidate) && $n < 100); return $candidate; }, 10, 5); /** In root mode, old /case-studies/slug/ links redirect (301) to /slug/. */ add_action('template_redirect', function () { if (!sahcs_root_mode() || !is_singular(SAHCS_CPT) || is_preview()) { return; } $request = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : ''; $path = trim((string) wp_parse_url($request, PHP_URL_PATH), '/'); $base = trim(sahcs_get_settings('archive_slug'), '/'); $home_path = trim((string) wp_parse_url(home_url(), PHP_URL_PATH), '/'); if ($home_path !== '' && strpos($path, $home_path . '/') === 0) { $path = substr($path, strlen($home_path) + 1); } if (strpos($path, $base . '/') === 0) { wp_safe_redirect(get_permalink(get_queried_object_id()), 301); exit; } }); /** Listing pages show the number of case studies chosen in settings. */ add_action('pre_get_posts', function ($q) { if (is_admin() || !$q->is_main_query()) { return; } if ($q->is_post_type_archive(SAHCS_CPT) || $q->is_tax(SAHCS_TAX)) { $q->set('posts_per_page', (int) sahcs_get_settings('per_page')); } }); /** * When a case study is saved, remember its headline result * (e.g. "+107% Conversion rate") so cards can show it without * re-reading the whole post. */ add_action('save_post_' . SAHCS_CPT, function ($post_id, $post) { if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) { return; } $highlight = sahcs_find_highlight($post->post_content); if ($highlight) { update_post_meta($post_id, '_sahcs_highlight', wp_slash($highlight)); } else { delete_post_meta($post_id, '_sahcs_highlight'); } }, 10, 2); function sahcs_find_highlight($content) { if (strpos($content, 'wp:sahcs/results') === false) { return null; } $found = null; $walk = function ($blocks) use (&$walk, &$found) { foreach ($blocks as $b) { if ($found) { return; } if ($b['blockName'] === 'sahcs/results' && !empty($b['attrs']['items'])) { // Same rule as the Results section: an item needs a label and an "after" value. $items = array_filter((array) $b['attrs']['items'], function ($i) { return is_array($i) && !empty($i['label']) && isset($i['after']) && $i['after'] !== ''; }); foreach ($items as $item) { $change = sahcs_metric_change($item); if ($change && $change['improved']) { $found = ['value' => $change['label'], 'label' => isset($item['label']) ? sanitize_text_field($item['label']) : '']; return; } } $first = reset($items); if ($first) { $found = ['value' => sanitize_text_field(sahcs_format_value($first['after'], $first)), 'label' => sanitize_text_field($first['label'])]; return; } } if (!empty($b['innerBlocks'])) { $walk($b['innerBlocks']); } } }; $walk(parse_blocks($content)); return $found; } /* * Case studies are built from SiteAuditHub Case Studies' sections, which live in the * block editor. When a plugin or theme switches the block editor off * (Classic Editor, Disable Gutenberg…), keep it on for case studies only - * posts and pages still use whatever the site has chosen. * Developers can opt out: add_filter('sahcs_use_block_editor', '__return_false'); */ add_filter('use_block_editor_for_post_type', function ($use, $post_type) { if ($post_type !== SAHCS_CPT) { return $use; } return (bool) apply_filters('sahcs_use_block_editor', true); }, 1000, 2); add_filter('use_block_editor_for_post', function ($use, $post) { if (!$post || get_post_type($post) !== SAHCS_CPT) { return $use; } return (bool) apply_filters('sahcs_use_block_editor', true); }, 1000, 2);