register_sitemap_rewrites(); flush_rewrite_rules(); } public static function deactivate() { flush_rewrite_rules(); } public function __construct() { add_action('admin_menu', [$this, 'register_admin_menu']); add_action('admin_init', [$this, 'register_settings']); add_action('add_meta_boxes', [$this, 'register_meta_boxes']); add_action('save_post', [$this, 'save_meta_box']); add_filter('wp_insert_post_data', [$this, 'maybe_switch_post_type'], 10, 2); add_action('show_user_profile', [$this, 'render_user_fields']); add_action('edit_user_profile', [$this, 'render_user_fields']); add_action('personal_options_update', [$this, 'save_user_fields']); add_action('edit_user_profile_update', [$this, 'save_user_fields']); add_action('created_term', [$this, 'save_term_fields'], 10, 3); add_action('edited_term', [$this, 'save_term_fields'], 10, 3); add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']); add_filter('pre_get_document_title', [$this, 'filter_document_title'], 20); add_action('wp_head', [$this, 'output_meta_description'], 1); add_filter('the_title', [$this, 'maybe_hide_page_title'], 10, 2); add_filter('single_post_title', [$this, 'maybe_hide_single_post_title'], 10, 2); add_action('admin_notices', [$this, 'settings_notice_if_no_types']); add_action('init', [$this, 'register_taxonomy_hooks']); add_action('init', [$this, 'register_sitemap_rewrites']); add_action('template_redirect', [$this, 'maybe_render_sitemap']); add_filter('query_vars', [$this, 'register_query_vars']); } public function get_settings() { $defaults = [ 'enable_meta_module' => 1, 'enable_sitemap_module' => 1, 'enabled_post_types' => $this->get_default_post_types(), 'enable_author_fields' => 1, 'enable_taxonomy_fields'=> 1, 'special_pages' => [ 'date' => ['title' => '', 'description' => ''], 'search'=> ['title' => '', 'description' => ''], '404' => ['title' => '', 'description' => ''], 'home' => ['title' => '', 'description' => ''], ], 'post_type_archives' => [], ]; $settings = get_option(self::OPTION_KEY, []); if (!is_array($settings)) { $settings = []; } $settings = wp_parse_args($settings, $defaults); $settings['enable_meta_module'] = empty($settings['enable_meta_module']) ? 0 : 1; $settings['enable_sitemap_module'] = empty($settings['enable_sitemap_module']) ? 0 : 1; $settings['enabled_post_types'] = array_values(array_filter(array_map('sanitize_key', (array) $settings['enabled_post_types']))); $settings['enable_author_fields'] = empty($settings['enable_author_fields']) ? 0 : 1; $settings['enable_taxonomy_fields'] = empty($settings['enable_taxonomy_fields']) ? 0 : 1; $settings['special_pages'] = $this->sanitize_special_pages($settings['special_pages']); $settings['post_type_archives'] = $this->sanitize_post_type_archive_settings($settings['post_type_archives']); return $settings; } private function get_public_post_types() { $post_types = get_post_types([ 'public' => true, 'show_ui' => true, ], 'objects'); unset($post_types['attachment']); return $post_types; } private function get_default_post_types() { return array_keys($this->get_public_post_types()); } private function get_public_taxonomies() { return get_taxonomies([ 'public' => true, 'show_ui' => true, ], 'objects'); } private function get_archive_post_types() { $post_types = $this->get_public_post_types(); foreach ($post_types as $key => $post_type) { if (empty($post_type->has_archive)) { unset($post_types[$key]); } } return $post_types; } private function sanitize_special_pages($special_pages) { $defaults = [ 'date' => ['title' => '', 'description' => ''], 'search' => ['title' => '', 'description' => ''], '404' => ['title' => '', 'description' => ''], 'home' => ['title' => '', 'description' => ''], ]; $special_pages = is_array($special_pages) ? $special_pages : []; foreach ($defaults as $key => $value) { $page = isset($special_pages[$key]) && is_array($special_pages[$key]) ? $special_pages[$key] : []; $defaults[$key] = [ 'title' => isset($page['title']) ? sanitize_text_field($page['title']) : '', 'description' => isset($page['description']) ? sanitize_textarea_field($page['description']) : '', ]; } return $defaults; } private function sanitize_post_type_archive_settings($archives) { $clean = []; $allowed = array_keys($this->get_archive_post_types()); $archives = is_array($archives) ? $archives : []; foreach ($allowed as $post_type) { $archive = isset($archives[$post_type]) && is_array($archives[$post_type]) ? $archives[$post_type] : []; $clean[$post_type] = [ 'title' => isset($archive['title']) ? sanitize_text_field($archive['title']) : '', 'description' => isset($archive['description']) ? sanitize_textarea_field($archive['description']) : '', ]; } return $clean; } public function register_admin_menu() { add_menu_page( __('Ansico WP Basic', 'ansico-wp-basic'), __('Ansico WP Basic', 'ansico-wp-basic'), 'manage_options', 'ansico-wp-basic', [$this, 'render_settings_page'], 'dashicons-search', 81 ); add_submenu_page( 'ansico-wp-basic', __('Settings', 'ansico-wp-basic'), __('Settings', 'ansico-wp-basic'), 'manage_options', 'ansico-wp-basic', [$this, 'render_settings_page'] ); } public function register_settings() { register_setting( 'ansico_wp_basic_settings_group', self::OPTION_KEY, [$this, 'sanitize_settings'] ); add_settings_section( 'ansico_wp_basic_main_section', __('General settings', 'ansico-wp-basic'), function () { echo '

' . esc_html__('Choose where the SEO fields should be available.', 'ansico-wp-basic') . '

'; }, 'ansico-wp-basic' ); add_settings_field( 'enable_meta_module', __('SEO module', 'ansico-wp-basic'), [$this, 'render_meta_module_toggle'], 'ansico-wp-basic', 'ansico_wp_basic_main_section' ); add_settings_field( 'enable_sitemap_module', __('XML sitemap module', 'ansico-wp-basic'), [$this, 'render_sitemap_module_toggle'], 'ansico-wp-basic', 'ansico_wp_basic_main_section' ); add_settings_field( 'enabled_post_types', __('Enable for post types', 'ansico-wp-basic'), [$this, 'render_post_types_field'], 'ansico-wp-basic', 'ansico_wp_basic_main_section' ); add_settings_field( 'enable_author_fields', __('Author archive SEO fields', 'ansico-wp-basic'), [$this, 'render_author_field_toggle'], 'ansico-wp-basic', 'ansico_wp_basic_main_section' ); add_settings_field( 'enable_taxonomy_fields', __('Taxonomy archive SEO fields', 'ansico-wp-basic'), [$this, 'render_taxonomy_field_toggle'], 'ansico-wp-basic', 'ansico_wp_basic_main_section' ); add_settings_section( 'ansico_wp_basic_archives_section', __('Archives and special pages', 'ansico-wp-basic'), function () { echo '

' . esc_html__('Set SEO title and description for archive-style pages that do not have a normal editor screen.', 'ansico-wp-basic') . '

'; }, 'ansico-wp-basic' ); add_settings_field( 'special_pages', __('Global archive pages', 'ansico-wp-basic'), [$this, 'render_special_pages_fields'], 'ansico-wp-basic', 'ansico_wp_basic_archives_section' ); add_settings_field( 'post_type_archives', __('Post type archives', 'ansico-wp-basic'), [$this, 'render_post_type_archive_fields'], 'ansico-wp-basic', 'ansico_wp_basic_archives_section' ); } public function sanitize_settings($input) { $public_post_types = array_keys($this->get_public_post_types()); $enabled_post_types = []; if (!empty($input['enabled_post_types']) && is_array($input['enabled_post_types'])) { foreach ($input['enabled_post_types'] as $post_type) { $post_type = sanitize_key($post_type); if (in_array($post_type, $public_post_types, true)) { $enabled_post_types[] = $post_type; } } } return [ 'enable_meta_module' => empty($input['enable_meta_module']) ? 0 : 1, 'enable_sitemap_module' => empty($input['enable_sitemap_module']) ? 0 : 1, 'enabled_post_types' => array_values(array_unique($enabled_post_types)), 'enable_author_fields' => empty($input['enable_author_fields']) ? 0 : 1, 'enable_taxonomy_fields' => empty($input['enable_taxonomy_fields']) ? 0 : 1, 'special_pages' => $this->sanitize_special_pages(isset($input['special_pages']) ? $input['special_pages'] : []), 'post_type_archives' => $this->sanitize_post_type_archive_settings(isset($input['post_type_archives']) ? $input['post_type_archives'] : []), ]; } public function render_meta_module_toggle() { $settings = $this->get_settings(); printf( '

%4$s

', esc_attr(self::OPTION_KEY), checked($settings['enable_meta_module'], 1, false), esc_html__('Enable Meta title and Meta description fields and frontend output.', 'ansico-wp-basic'), esc_html__('Turn this off to hide the SEO editor UI and stop custom SEO output on the frontend, while preserving saved values.', 'ansico-wp-basic') ); } public function render_sitemap_module_toggle() { $settings = $this->get_settings(); printf( '

%4$s

', esc_attr(self::OPTION_KEY), checked($settings['enable_sitemap_module'], 1, false), esc_html__('Enable XML sitemap generation.', 'ansico-wp-basic'), esc_html__('When enabled, the sitemap index is available at /ansico-sitemap.xml.', 'ansico-wp-basic') ); } public function render_post_types_field() { $settings = $this->get_settings(); $public_post_types = $this->get_public_post_types(); echo '
'; foreach ($public_post_types as $post_type) { printf( '', esc_attr(self::OPTION_KEY), esc_attr($post_type->name), checked(in_array($post_type->name, $settings['enabled_post_types'], true), true, false), esc_html($post_type->labels->singular_name) ); } echo '
'; } public function render_author_field_toggle() { $settings = $this->get_settings(); printf( '

%4$s

', esc_attr(self::OPTION_KEY), checked($settings['enable_author_fields'], 1, false), esc_html__('Allow SEO fields on user profile pages for author archives.', 'ansico-wp-basic'), esc_html__('When enabled, each user profile gets Meta title and Meta description fields for that author archive.', 'ansico-wp-basic') ); } public function render_taxonomy_field_toggle() { $settings = $this->get_settings(); printf( '

%4$s

', esc_attr(self::OPTION_KEY), checked($settings['enable_taxonomy_fields'], 1, false), esc_html__('Allow SEO fields on taxonomy term pages.', 'ansico-wp-basic'), esc_html__('Works for categories, tags, and public custom taxonomies.', 'ansico-wp-basic') ); } public function render_special_pages_fields() { $settings = $this->get_settings(); if (empty($settings['enable_meta_module'])) { echo '

' . esc_html__('The SEO module is currently disabled.', 'ansico-wp-basic') . '

'; return; } $pages = [ 'home' => __('Blog home / posts page', 'ansico-wp-basic'), 'date' => __('Date archives', 'ansico-wp-basic'), 'search' => __('Search results pages', 'ansico-wp-basic'), '404' => __('404 page', 'ansico-wp-basic'), ]; echo '
'; foreach ($pages as $key => $label) { $title = $settings['special_pages'][$key]['title'] ?? ''; $description = $settings['special_pages'][$key]['description'] ?? ''; printf( '

%1$s

', esc_html($label), esc_html__('Meta title', 'ansico-wp-basic'), esc_attr(self::OPTION_KEY), esc_attr($key), esc_attr($title), esc_attr__('Enter a custom meta title', 'ansico-wp-basic'), esc_html__('Meta description', 'ansico-wp-basic'), esc_attr__('Enter a custom meta description', 'ansico-wp-basic'), esc_textarea($description) ); } echo '
'; } public function render_post_type_archive_fields() { $settings = $this->get_settings(); if (empty($settings['enable_meta_module'])) { echo '

' . esc_html__('The SEO module is currently disabled.', 'ansico-wp-basic') . '

'; return; } $archives = $this->get_archive_post_types(); if (empty($archives)) { echo '

' . esc_html__('No public post type archives were found.', 'ansico-wp-basic') . '

'; return; } echo '
'; foreach ($archives as $post_type) { $title = $settings['post_type_archives'][$post_type->name]['title'] ?? ''; $description = $settings['post_type_archives'][$post_type->name]['description'] ?? ''; printf( '

%1$s (%2$s)

', esc_html($post_type->labels->name), esc_attr($post_type->name), esc_html__('Meta title', 'ansico-wp-basic'), esc_attr(self::OPTION_KEY), esc_attr($title), esc_attr__('Enter a custom meta title', 'ansico-wp-basic'), esc_html__('Meta description', 'ansico-wp-basic'), esc_attr__('Enter a custom meta description', 'ansico-wp-basic'), esc_textarea($description) ); } echo '
'; } public function render_settings_page() { if (!current_user_can('manage_options')) { return; } ?>

get_settings(); ?>

' . esc_html(home_url('/ansico-sitemap.xml')) . '' : esc_html__('Disabled', 'ansico-wp-basic'); ?>

get_settings(); foreach ($settings['enabled_post_types'] as $post_type) { if (!empty($settings['enable_meta_module'])) { add_meta_box( 'ansico_wp_basic_seo', __('Ansico WP Basic SEO', 'ansico-wp-basic'), [$this, 'render_meta_box'], $post_type, 'normal', 'default' ); } add_meta_box( 'ansico_wp_basic_tools', __('Ansico WP Basic Tools', 'ansico-wp-basic'), [$this, 'render_tools_meta_box'], $post_type, 'side', 'default' ); } } private function get_switchable_post_types() { $post_types = $this->get_public_post_types(); return array_filter($post_types, function($post_type) { return !empty($post_type->show_ui); }); } public function render_tools_meta_box($post) { wp_nonce_field('ansico_wp_basic_save_meta_box', self::NONCE_KEY); $post_type = get_post_type($post); $switchable_types = $this->get_switchable_post_types(); $hide_title = (int) get_post_meta($post->ID, self::HIDE_TITLE_KEY, true); echo '
'; if ($post_type === 'page') { echo '

'; } echo '

'; echo ''; echo '

'; } private function render_snippet_box($args) { $meta_title = isset($args['meta_title']) ? (string) $args['meta_title'] : ''; $meta_description = isset($args['meta_description']) ? (string) $args['meta_description'] : ''; $fallback_title = isset($args['fallback_title']) ? (string) $args['fallback_title'] : ''; $permalink = isset($args['permalink']) ? (string) $args['permalink'] : home_url('/'); $site_name = get_bloginfo('name'); ?>

(int) floor($limit * 0.6)) { $trimmed = mb_substr($trimmed, 0, $last_space); } else { $trimmed = mb_substr($trimmed, 0, $limit); } return rtrim($trimmed, " .,;:-"); } if (strlen($text) <= $limit) { return $text; } $trimmed = substr($text, 0, $limit + 1); $last_space = strrpos($trimmed, ' '); if ($last_space !== false && $last_space > (int) floor($limit * 0.6)) { $trimmed = substr($trimmed, 0, $last_space); } else { $trimmed = substr($trimmed, 0, $limit); } return rtrim($trimmed, " .,;:-"); } private function get_default_meta_description_for_post($post) { $content = isset($post->post_content) ? (string) $post->post_content : ''; $first_paragraph = ''; if (preg_match('/]*>(.*?)<\/p>/is', $content, $matches)) { $first_paragraph = wp_strip_all_tags($matches[1]); } if ($first_paragraph === '') { $blocks = preg_split('/ \s* /', wp_strip_all_tags($content)); if (is_array($blocks)) { foreach ($blocks as $block) { $block = trim((string) $block); if ($block !== '') { $first_paragraph = $block; break; } } } } if ($first_paragraph === '') { $first_paragraph = has_excerpt($post) ? $post->post_excerpt : wp_trim_words(wp_strip_all_tags($content), 30, ''); } return $this->get_trimmed_text_excerpt($first_paragraph, 150); } private function build_document_title_from_meta($meta_title) { $meta_title = trim((string) $meta_title); if ($meta_title === '') { return ''; } $site_name = trim((string) get_bloginfo('name')); if ($site_name === '') { return wp_strip_all_tags($meta_title); } $title_plain = wp_strip_all_tags($meta_title); if (stripos($title_plain, $site_name) !== false) { return $title_plain; } return $title_plain . ' - ' . $site_name; } public function render_meta_box($post) { wp_nonce_field('ansico_wp_basic_save_meta_box', self::NONCE_KEY); $saved_meta_title = get_post_meta($post->ID, self::META_TITLE_KEY, true); $saved_meta_description = get_post_meta($post->ID, self::META_DESC_KEY, true); $fallback_title = get_the_title($post); $meta_title = $saved_meta_title !== '' ? $saved_meta_title : $fallback_title; $meta_description = $saved_meta_description !== '' ? $saved_meta_description : $this->get_default_meta_description_for_post($post); $permalink = get_permalink($post) ?: home_url('/'); $this->render_snippet_box([ 'meta_title' => $meta_title, 'meta_description' => $meta_description, 'fallback_title' => $fallback_title, 'permalink' => $permalink, ]); } public function save_meta_box($post_id) { if (!isset($_POST[self::NONCE_KEY]) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST[self::NONCE_KEY])), 'ansico_wp_basic_save_meta_box')) { return; } if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } if (!current_user_can('edit_post', $post_id)) { return; } $post_type = get_post_type($post_id); $settings = $this->get_settings(); if (!in_array($post_type, $settings['enabled_post_types'], true)) { return; } if ($post_type === 'page') { $hide_title = isset($_POST['ansico_wp_basic_hide_title']) ? 1 : 0; if ($hide_title) { update_post_meta($post_id, self::HIDE_TITLE_KEY, 1); } else { delete_post_meta($post_id, self::HIDE_TITLE_KEY); } } if (empty($settings['enable_meta_module'])) { return; } $meta_title = isset($_POST['ansico_wp_basic_meta_title']) ? sanitize_text_field(wp_unslash($_POST['ansico_wp_basic_meta_title'])) : ''; $meta_description = isset($_POST['ansico_wp_basic_meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['ansico_wp_basic_meta_description'])) : ''; if ($meta_title === '') { delete_post_meta($post_id, self::META_TITLE_KEY); } else { update_post_meta($post_id, self::META_TITLE_KEY, $meta_title); } if ($meta_description === '') { delete_post_meta($post_id, self::META_DESC_KEY); } else { update_post_meta($post_id, self::META_DESC_KEY, $meta_description); } } public function maybe_switch_post_type($data, $postarr) { if (is_admin() && isset($_POST[self::NONCE_KEY]) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST[self::NONCE_KEY])), 'ansico_wp_basic_save_meta_box')) { if (!empty($postarr['ID']) && current_user_can('edit_post', (int) $postarr['ID']) && !empty($_POST['ansico_wp_basic_change_post_type'])) { $new_post_type = sanitize_key(wp_unslash($_POST['ansico_wp_basic_change_post_type'])); $allowed_types = array_keys($this->get_switchable_post_types()); if (in_array($new_post_type, $allowed_types, true)) { $data['post_type'] = $new_post_type; } } } return $data; } public function maybe_hide_page_title($title, $post_id = 0) { if (is_admin() || !is_singular('page')) { return $title; } $queried_id = get_queried_object_id(); if (!$queried_id || (int) $post_id !== (int) $queried_id) { return $title; } if ((int) get_post_meta($queried_id, self::HIDE_TITLE_KEY, true) === 1) { return ''; } return $title; } public function maybe_hide_single_post_title($title, $post = null) { if (is_admin() || !is_singular('page')) { return $title; } $queried_id = get_queried_object_id(); if ($queried_id && (int) get_post_meta($queried_id, self::HIDE_TITLE_KEY, true) === 1) { return ''; } return $title; } public function register_taxonomy_hooks() { foreach ($this->get_public_taxonomies() as $taxonomy) { add_action($taxonomy->name . '_add_form_fields', [$this, 'render_term_add_fields']); add_action($taxonomy->name . '_edit_form_fields', [$this, 'render_term_edit_fields']); } } public function render_term_add_fields($taxonomy) { $settings = $this->get_settings(); if (empty($settings['enable_meta_module']) || empty($settings['enable_taxonomy_fields'])) { return; } $taxonomy_name = is_string($taxonomy) ? $taxonomy : ''; $example_url = home_url('/' . $taxonomy_name . '/example/'); wp_nonce_field('ansico_wp_basic_save_term_fields', self::TERM_NONCE_KEY); ?>

render_snippet_box([ 'meta_title' => '', 'meta_description' => '', 'fallback_title' => ucfirst($taxonomy_name) . ' archive', 'permalink' => $example_url, ]); ?>
get_settings(); if (empty($settings['enable_meta_module']) || empty($settings['enable_taxonomy_fields'])) { return; } $meta_title = get_term_meta($term->term_id, self::META_TITLE_KEY, true); $meta_description = get_term_meta($term->term_id, self::META_DESC_KEY, true); wp_nonce_field('ansico_wp_basic_save_term_fields', self::TERM_NONCE_KEY); ?> render_snippet_box([ 'meta_title' => $meta_title, 'meta_description' => $meta_description, 'fallback_title' => $term->name, 'permalink' => (!is_wp_error(get_term_link($term)) ? get_term_link($term) : home_url('/')), ]); ?> get_settings(); if (empty($settings['enable_meta_module']) || empty($settings['enable_taxonomy_fields'])) { return; } $taxonomy_object = $taxonomy ? get_taxonomy($taxonomy) : null; if ($taxonomy_object && (!is_object($taxonomy_object) || empty($taxonomy_object->public) || empty($taxonomy_object->show_ui))) { return; } if (!isset($_POST[self::TERM_NONCE_KEY]) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST[self::TERM_NONCE_KEY])), 'ansico_wp_basic_save_term_fields')) { return; } if (!current_user_can('manage_categories')) { return; } $meta_title = isset($_POST['ansico_wp_basic_meta_title']) ? sanitize_text_field(wp_unslash($_POST['ansico_wp_basic_meta_title'])) : ''; $meta_description = isset($_POST['ansico_wp_basic_meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['ansico_wp_basic_meta_description'])) : ''; if ($meta_title === '') { delete_term_meta($term_id, self::META_TITLE_KEY); } else { update_term_meta($term_id, self::META_TITLE_KEY, $meta_title); } if ($meta_description === '') { delete_term_meta($term_id, self::META_DESC_KEY); } else { update_term_meta($term_id, self::META_DESC_KEY, $meta_description); } } public function render_user_fields($user) { $settings = $this->get_settings(); if (empty($settings['enable_meta_module']) || empty($settings['enable_author_fields'])) { return; } wp_nonce_field('ansico_wp_basic_save_user_fields', self::USER_NONCE_KEY); $meta_title = get_user_meta($user->ID, self::META_TITLE_KEY, true); $meta_description = get_user_meta($user->ID, self::META_DESC_KEY, true); ?>

render_snippet_box([ 'meta_title' => $meta_title, 'meta_description' => $meta_description, 'fallback_title' => $user->display_name, 'permalink' => get_author_posts_url($user->ID), ]); ?> get_settings(); if (empty($settings['enable_meta_module']) || empty($settings['enable_author_fields'])) { return; } if (!current_user_can('edit_user', $user_id)) { return; } if (!isset($_POST[self::USER_NONCE_KEY]) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST[self::USER_NONCE_KEY])), 'ansico_wp_basic_save_user_fields')) { return; } $meta_title = isset($_POST['ansico_wp_basic_meta_title']) ? sanitize_text_field(wp_unslash($_POST['ansico_wp_basic_meta_title'])) : ''; $meta_description = isset($_POST['ansico_wp_basic_meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['ansico_wp_basic_meta_description'])) : ''; if ($meta_title === '') { delete_user_meta($user_id, self::META_TITLE_KEY); } else { update_user_meta($user_id, self::META_TITLE_KEY, $meta_title); } if ($meta_description === '') { delete_user_meta($user_id, self::META_DESC_KEY); } else { update_user_meta($user_id, self::META_DESC_KEY, $meta_description); } } public function enqueue_admin_assets($hook) { $allowed_hooks = [ 'post.php', 'post-new.php', 'profile.php', 'user-edit.php', 'edit-tags.php', 'term.php', 'toplevel_page_ansico-wp-basic', ]; if (!in_array($hook, $allowed_hooks, true)) { return; } wp_enqueue_style( 'ansico-wp-basic-admin', plugin_dir_url(__FILE__) . 'assets/admin.css', [], '0.0.0.2' ); wp_enqueue_script( 'ansico-wp-basic-admin', plugin_dir_url(__FILE__) . 'assets/admin.js', [], '0.0.0.2', true ); } private function get_context_meta_title() { $settings = $this->get_settings(); if (empty($settings['enable_meta_module'])) { return ''; } if (is_singular()) { $post_id = get_queried_object_id(); $post_type = get_post_type($post_id); if ($post_id && in_array($post_type, $settings['enabled_post_types'], true)) { return get_post_meta($post_id, self::META_TITLE_KEY, true); } } if (is_author() && !empty($settings['enable_author_fields'])) { $author = get_queried_object(); if ($author instanceof WP_User || (is_object($author) && isset($author->ID))) { return get_user_meta((int) $author->ID, self::META_TITLE_KEY, true); } } if ((is_category() || is_tag() || is_tax()) && !empty($settings['enable_taxonomy_fields'])) { $term = get_queried_object(); if ($term instanceof WP_Term || (is_object($term) && isset($term->term_id))) { return get_term_meta((int) $term->term_id, self::META_TITLE_KEY, true); } } if (is_post_type_archive()) { $post_type = get_query_var('post_type'); $post_type = is_array($post_type) ? reset($post_type) : $post_type; return $settings['post_type_archives'][$post_type]['title'] ?? ''; } if (is_home()) { return $settings['special_pages']['home']['title'] ?? ''; } if (is_date()) { return $settings['special_pages']['date']['title'] ?? ''; } if (is_search()) { return $settings['special_pages']['search']['title'] ?? ''; } if (is_404()) { return $settings['special_pages']['404']['title'] ?? ''; } return ''; } private function get_context_meta_description() { $settings = $this->get_settings(); if (empty($settings['enable_meta_module'])) { return ''; } if (is_singular()) { $post_id = get_queried_object_id(); $post_type = get_post_type($post_id); if ($post_id && in_array($post_type, $settings['enabled_post_types'], true)) { return get_post_meta($post_id, self::META_DESC_KEY, true); } } if (is_author() && !empty($settings['enable_author_fields'])) { $author = get_queried_object(); if ($author instanceof WP_User || (is_object($author) && isset($author->ID))) { return get_user_meta((int) $author->ID, self::META_DESC_KEY, true); } } if ((is_category() || is_tag() || is_tax()) && !empty($settings['enable_taxonomy_fields'])) { $term = get_queried_object(); if ($term instanceof WP_Term || (is_object($term) && isset($term->term_id))) { return get_term_meta((int) $term->term_id, self::META_DESC_KEY, true); } } if (is_post_type_archive()) { $post_type = get_query_var('post_type'); $post_type = is_array($post_type) ? reset($post_type) : $post_type; return $settings['post_type_archives'][$post_type]['description'] ?? ''; } if (is_home()) { return $settings['special_pages']['home']['description'] ?? ''; } if (is_date()) { return $settings['special_pages']['date']['description'] ?? ''; } if (is_search()) { return $settings['special_pages']['search']['description'] ?? ''; } if (is_404()) { return $settings['special_pages']['404']['description'] ?? ''; } return ''; } public function filter_document_title($title) { if (is_admin() || is_feed()) { return $title; } $meta_title = $this->get_context_meta_title(); if (!empty($meta_title)) { return $this->build_document_title_from_meta($meta_title); } return $title; } public function output_meta_description() { if (is_admin() || is_feed()) { return; } $meta_description = $this->get_context_meta_description(); if (!empty($meta_description)) { echo "\n" . '' . "\n"; } } public function register_query_vars($vars) { $vars[] = 'ansico_wp_basic_sitemap'; $vars[] = 'ansico_wp_basic_sitemap_name'; return $vars; } public function register_sitemap_rewrites() { $settings = $this->get_settings(); if (empty($settings['enable_sitemap_module'])) { return; } add_rewrite_rule('^ansico-sitemap\.xml$', 'index.php?ansico_wp_basic_sitemap=index', 'top'); add_rewrite_rule('^ansico-sitemap-([a-z0-9_-]+)-([a-z0-9_-]+)\.xml$', 'index.php?ansico_wp_basic_sitemap=$matches[1]&ansico_wp_basic_sitemap_name=$matches[2]', 'top'); } public function maybe_render_sitemap() { $settings = $this->get_settings(); if (empty($settings['enable_sitemap_module'])) { return; } $type = get_query_var('ansico_wp_basic_sitemap'); if (!$type) { return; } $name = get_query_var('ansico_wp_basic_sitemap_name'); nocache_headers(); header('Content-Type: application/xml; charset=' . get_bloginfo('charset'), true); if ($type === 'index') { echo $this->get_sitemap_index_xml(); exit; } echo $this->get_sitemap_section_xml($type, $name); exit; } private function get_sitemap_index_xml() { $items = []; $settings = $this->get_settings(); if (empty($settings['enable_sitemap_module'])) { return ''; } foreach ($settings['enabled_post_types'] as $post_type) { $object = get_post_type_object($post_type); if (!$object || !$this->post_type_has_published_content($post_type)) { continue; } $items[] = [ 'loc' => home_url('/ansico-sitemap-post-' . $post_type . '.xml'), 'lastmod' => $this->get_latest_post_modified_gmt($post_type), ]; } if (!empty($settings['enable_taxonomy_fields'])) { foreach ($this->get_public_taxonomies() as $taxonomy) { if (!$this->taxonomy_has_terms($taxonomy->name)) { continue; } $items[] = [ 'loc' => home_url('/ansico-sitemap-taxonomy-' . $taxonomy->name . '.xml'), 'lastmod' => current_time('mysql', true), ]; } } if (!empty($settings['enable_author_fields']) && $this->site_has_authors_with_posts()) { $items[] = [ 'loc' => home_url('/ansico-sitemap-author-users.xml'), 'lastmod' => current_time('mysql', true), ]; } $items[] = [ 'loc' => home_url('/ansico-sitemap-page-archives.xml'), 'lastmod' => current_time('mysql', true), ]; $xml = '' . "\n"; $xml .= ''; foreach ($items as $item) { $xml .= ''; $xml .= '' . esc_url($item['loc']) . ''; if (!empty($item['lastmod'])) { $xml .= '' . esc_html(mysql2date('c', $item['lastmod'], false)) . ''; } $xml .= ''; } $xml .= ''; return $xml; } private function get_sitemap_section_xml($type, $name) { $urls = []; if ($type === 'post') { $urls = $this->get_post_sitemap_urls($name); } elseif ($type === 'taxonomy') { $urls = $this->get_taxonomy_sitemap_urls($name); } elseif ($type === 'author' && $name === 'users') { $urls = $this->get_author_sitemap_urls(); } elseif ($type === 'page' && $name === 'archives') { $urls = $this->get_archive_sitemap_urls(); } $xml = '' . "\n"; $xml .= ''; foreach ($urls as $url) { $xml .= ''; $xml .= '' . esc_url($url['loc']) . ''; if (!empty($url['lastmod'])) { $xml .= '' . esc_html(mysql2date('c', $url['lastmod'], false)) . ''; } if (!empty($url['changefreq'])) { $xml .= '' . esc_html($url['changefreq']) . ''; } if (isset($url['priority'])) { $xml .= '' . esc_html(number_format((float) $url['priority'], 1, '.', '')) . ''; } $xml .= ''; } $xml .= ''; return $xml; } private function post_type_has_published_content($post_type) { global $wpdb; $count = (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish'", $post_type )); return $count > 0; } private function get_latest_post_modified_gmt($post_type) { global $wpdb; $lastmod = $wpdb->get_var($wpdb->prepare( "SELECT post_modified_gmt FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish' ORDER BY post_modified_gmt DESC LIMIT 1", $post_type )); return $lastmod ?: current_time('mysql', true); } private function taxonomy_has_terms($taxonomy) { $terms = get_terms([ 'taxonomy' => $taxonomy, 'hide_empty' => true, 'number' => 1, 'fields' => 'ids', ]); return !is_wp_error($terms) && !empty($terms); } private function site_has_authors_with_posts() { $users = get_users([ 'has_published_posts' => array_keys($this->get_public_post_types()), 'number' => 1, 'fields' => 'ids', ]); return !empty($users); } private function get_post_sitemap_urls($post_type) { $settings = $this->get_settings(); if (!in_array($post_type, $settings['enabled_post_types'], true)) { return []; } $posts = get_posts([ 'post_type' => $post_type, 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'modified', 'order' => 'DESC', 'fields' => 'all', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ]); $urls = []; foreach ($posts as $post) { $loc = get_permalink($post); if (!$loc) { continue; } $urls[] = [ 'loc' => $loc, 'lastmod' => get_post_modified_time('Y-m-d H:i:s', true, $post), 'changefreq' => 'weekly', 'priority' => $post_type === 'page' ? 0.8 : 0.6, ]; } return $urls; } private function get_taxonomy_sitemap_urls($taxonomy) { $settings = $this->get_settings(); if (empty($settings['enable_taxonomy_fields'])) { return []; } $taxonomy_object = get_taxonomy($taxonomy); if (!$taxonomy_object || empty($taxonomy_object->public)) { return []; } $terms = get_terms([ 'taxonomy' => $taxonomy, 'hide_empty' => true, 'number' => 0, ]); if (is_wp_error($terms) || empty($terms)) { return []; } $urls = []; foreach ($terms as $term) { $loc = get_term_link($term); if (is_wp_error($loc)) { continue; } $urls[] = [ 'loc' => $loc, 'lastmod' => current_time('mysql', true), 'changefreq' => 'weekly', 'priority' => 0.5, ]; } return $urls; } private function get_author_sitemap_urls() { $settings = $this->get_settings(); if (empty($settings['enable_author_fields'])) { return []; } $users = get_users([ 'has_published_posts' => array_keys($this->get_public_post_types()), 'fields' => ['ID'], ]); $urls = []; foreach ($users as $user) { $urls[] = [ 'loc' => get_author_posts_url($user->ID), 'lastmod' => current_time('mysql', true), 'changefreq' => 'weekly', 'priority' => 0.4, ]; } return $urls; } private function get_archive_sitemap_urls() { $urls = [[ 'loc' => home_url('/'), 'lastmod' => current_time('mysql', true), 'changefreq' => 'daily', 'priority' => 1.0, ]]; if (get_option('show_on_front') === 'page') { $page_for_posts = (int) get_option('page_for_posts'); if ($page_for_posts) { $loc = get_permalink($page_for_posts); if ($loc) { $urls[] = [ 'loc' => $loc, 'lastmod' => get_post_modified_time('Y-m-d H:i:s', true, $page_for_posts), 'changefreq' => 'daily', 'priority' => 0.8, ]; } } } foreach ($this->get_archive_post_types() as $post_type) { $loc = get_post_type_archive_link($post_type->name); if ($loc) { $urls[] = [ 'loc' => $loc, 'lastmod' => $this->get_latest_post_modified_gmt($post_type->name), 'changefreq' => 'daily', 'priority' => 0.7, ]; } } return $urls; } public function settings_notice_if_no_types() { $screen = function_exists('get_current_screen') ? get_current_screen() : null; if (!$screen || $screen->id !== 'toplevel_page_ansico-wp-basic') { return; } $settings = $this->get_settings(); if (!empty($settings['enabled_post_types'])) { return; } echo '

' . esc_html__('No post types are enabled. Select at least one post type to use the SEO fields on content edit screens.', 'ansico-wp-basic') . '

'; } } new Ansico_WP_Basic();