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('admin_init', [$this, 'maybe_handle_yoast_import']);
add_action('admin_init', [$this, 'maybe_handle_generate_missing_meta']);
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', [$this, 'maybe_disable_core_canonical'], 1);
add_action('wp_head', [$this, 'output_meta_description'], 1);
add_action('wp_head', [$this, 'output_frontend_meta_tags'], 2);
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('admin_notices', [$this, 'render_import_notice']);
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,
'social_defaults' => [
'facebook_publisher_url' => '',
'x_site_handle' => '',
'default_social_image' => '',
],
'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['social_defaults'] = $this->sanitize_social_defaults($settings['social_defaults'] ?? []);
$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_social_defaults($social_defaults) {
$social_defaults = is_array($social_defaults) ? $social_defaults : [];
return [
'facebook_publisher_url' => isset($social_defaults['facebook_publisher_url']) ? esc_url_raw($social_defaults['facebook_publisher_url']) : '',
'x_site_handle' => isset($social_defaults['x_site_handle']) ? sanitize_text_field($social_defaults['x_site_handle']) : '',
'default_social_image' => isset($social_defaults['default_social_image']) ? esc_url_raw($social_defaults['default_social_image']) : '',
'organization_name' => isset($social_defaults['organization_name']) ? sanitize_text_field($social_defaults['organization_name']) : '',
'organization_logo' => isset($social_defaults['organization_logo']) ? esc_url_raw($social_defaults['organization_logo']) : '',
];
}
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_social_section',
__('Social tags and schema', 'ansico-wp-basic'),
function () {
echo '' . esc_html__('Configure site-wide defaults used for Open Graph, Twitter/X cards, canonical URLs, and schema markup output.', 'ansico-wp-basic') . '
';
},
'ansico-wp-basic'
);
add_settings_field(
'social_defaults',
__('Social defaults', 'ansico-wp-basic'),
[$this, 'render_social_defaults_fields'],
'ansico-wp-basic',
'ansico_wp_basic_social_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'
);
add_settings_section(
'ansico_wp_basic_tools_section',
__('Import tools', 'ansico-wp-basic'),
function () {
echo '' . esc_html__('Import existing SEO title, meta description, social defaults, canonical URLs, and social images from another plugin into Ansico WP Basic.', 'ansico-wp-basic') . '
';
},
'ansico-wp-basic'
);
add_settings_field(
'yoast_import',
__('Import from Yoast SEO', 'ansico-wp-basic'),
[$this, 'render_yoast_import_field'],
'ansico-wp-basic',
'ansico_wp_basic_tools_section'
);
add_settings_field(
'generate_missing_meta',
__('Generate missing META fields', 'ansico-wp-basic'),
[$this, 'render_generate_missing_meta_field'],
'ansico-wp-basic',
'ansico_wp_basic_tools_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,
'social_defaults' => $this->sanitize_social_defaults(isset($input['social_defaults']) ? $input['social_defaults'] : []),
'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(
' %3$s%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();
$sitemap_url = home_url('/sitemap.xml');
printf(
' %3$s%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__('Generate a sitemap index for supported content types.', 'ansico-wp-basic')
);
if (!empty($settings['enable_sitemap_module'])) {
echo '';
echo '' . esc_html__('Sitemap URL:', 'ansico-wp-basic') . ' ';
echo '' . esc_html($sitemap_url) . '';
echo '';
echo ' ';
echo ' ';
echo '
';
echo '' . esc_html__('Use this URL when submitting your sitemap to search engines or external tools.', '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(
' %4$s (%2$s) ',
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 ' ';
echo '' . esc_html__('Only selected post types will get Ansico SEO fields and bulk generation tools.', 'ansico-wp-basic') . '
';
}
public function render_author_field_toggle() {
$settings = $this->get_settings();
printf(
' %3$s%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(
' %3$s%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_social_defaults_fields() {
$settings = $this->get_settings();
$social = $settings['social_defaults'] ?? [];
echo '';
}
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(
'
',
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 '
';
}
private function is_yoast_available_for_import() {
if (defined('WPSEO_VERSION') || class_exists('WPSEO_Options')) {
return true;
}
if (get_option('wpseo') || get_option('wpseo_taxonomy_meta') || get_option('wpseo_titles')) {
return true;
}
$active_plugins = (array) get_option('active_plugins', []);
if (in_array('wordpress-seo/wp-seo.php', $active_plugins, true)) {
return true;
}
if (is_multisite()) {
$network_active = array_keys((array) get_site_option('active_sitewide_plugins', []));
if (in_array('wordpress-seo/wp-seo.php', $network_active, true)) {
return true;
}
}
return false;
}
public function render_yoast_import_field() {
if (!current_user_can('manage_options')) {
return;
}
if (!$this->is_yoast_available_for_import()) {
echo '' . esc_html__('Yoast SEO was not detected on this site, and no Yoast SEO data was found in the database.', 'ansico-wp-basic') . '
';
return;
}
$url = wp_nonce_url(
add_query_arg([
'page' => 'ansico-wp-basic',
'tab' => 'tools',
'action' => self::IMPORT_ACTION,
], admin_url('admin.php')),
self::IMPORT_ACTION,
'ansico_wp_basic_import_nonce'
);
echo '';
}
public function render_generate_missing_meta_field() {
if (!current_user_can('manage_options')) {
return;
}
$settings = $this->get_settings();
if (empty($settings['enable_meta_module'])) {
echo '' . esc_html__('The SEO module is currently disabled.', 'ansico-wp-basic') . '
';
return;
}
$enabled_types = !empty($settings['enabled_post_types']) ? $settings['enabled_post_types'] : [];
$labels = [];
foreach ($enabled_types as $post_type) {
$obj = get_post_type_object($post_type);
if ($obj && !empty($obj->labels->singular_name)) {
$labels[] = $obj->labels->singular_name;
} else {
$labels[] = $post_type;
}
}
$url = wp_nonce_url(
add_query_arg([
'page' => 'ansico-wp-basic',
'tab' => 'tools',
'action' => self::GENERATE_ACTION,
], admin_url('admin.php')),
self::GENERATE_ACTION,
'ansico_wp_basic_generate_nonce'
);
echo '';
}
public function maybe_handle_yoast_import() {
if (!is_admin() || !current_user_can('manage_options')) {
return;
}
if (!isset($_GET['page'], $_GET['action']) || $_GET['page'] !== 'ansico-wp-basic' || $_GET['action'] !== self::IMPORT_ACTION) {
return;
}
check_admin_referer(self::IMPORT_ACTION, 'ansico_wp_basic_import_nonce');
$result = $this->import_yoast_meta_data();
$redirect_url = add_query_arg([
'page' => 'ansico-wp-basic',
'tab' => 'tools',
'ansico_wp_basic_notice' => rawurlencode(wp_json_encode($result)),
], admin_url('admin.php'));
wp_safe_redirect($redirect_url);
exit;
}
private function import_yoast_meta_data() {
$overwrite_existing = false;
$result = [
'type' => 'success',
'message' => __('Yoast SEO data import completed.', 'ansico-wp-basic'),
'posts_imported' => 0,
'terms_imported' => 0,
'posts_skipped' => 0,
'terms_skipped' => 0,
'settings_imported' => 0,
];
$settings = $this->get_settings();
$yoast_social = get_option('wpseo_social', []);
if (is_array($yoast_social)) {
$site_settings_imported = 0;
if (empty($settings['social_defaults']['facebook_publisher_url']) && !empty($yoast_social['facebook_site'])) {
$settings['social_defaults']['facebook_publisher_url'] = esc_url_raw($yoast_social['facebook_site']);
$site_settings_imported++;
}
if (empty($settings['social_defaults']['x_site_handle']) && !empty($yoast_social['twitter_site'])) {
$settings['social_defaults']['x_site_handle'] = $this->normalize_x_handle($yoast_social['twitter_site']);
$site_settings_imported++;
}
if (empty($settings['social_defaults']['default_social_image']) && !empty($yoast_social['og_default_image'])) {
$settings['social_defaults']['default_social_image'] = esc_url_raw($yoast_social['og_default_image']);
$site_settings_imported++;
}
if ($site_settings_imported > 0) {
update_option(self::OPTION_KEY, $settings);
$result['settings_imported'] = $site_settings_imported;
}
}
$public_post_types = array_keys($this->get_public_post_types());
if (!empty($public_post_types)) {
$posts = get_posts([
'post_type' => $public_post_types,
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
'suppress_filters' => true,
]);
foreach ((array) $posts as $post_id) {
$yoast_title = get_post_meta($post_id, '_yoast_wpseo_title', true);
$yoast_description = get_post_meta($post_id, '_yoast_wpseo_metadesc', true);
$yoast_canonical = get_post_meta($post_id, '_yoast_wpseo_canonical', true);
$yoast_social_image = get_post_meta($post_id, '_yoast_wpseo_opengraph-image', true);
if ($yoast_social_image === '') {
$yoast_social_image = get_post_meta($post_id, '_yoast_wpseo_opengraph-image-url', true);
}
if ($yoast_title === '' && $yoast_description === '' && $yoast_canonical === '' && $yoast_social_image === '') {
continue;
}
$existing_title = get_post_meta($post_id, self::META_TITLE_KEY, true);
$existing_description = get_post_meta($post_id, self::META_DESC_KEY, true);
$existing_canonical = get_post_meta($post_id, self::CANONICAL_KEY, true);
$existing_social_image = get_post_meta($post_id, self::SOCIAL_IMAGE_KEY, true);
if ($yoast_title !== '' && ($overwrite_existing || $existing_title === '')) {
update_post_meta($post_id, self::META_TITLE_KEY, sanitize_text_field($yoast_title));
$result['posts_imported']++;
}
elseif ($yoast_title !== '') {
$result['posts_skipped']++;
}
if ($yoast_description !== '' && ($overwrite_existing || $existing_description === '')) {
update_post_meta($post_id, self::META_DESC_KEY, sanitize_textarea_field($yoast_description));
$result['posts_imported']++;
}
elseif ($yoast_description !== '') {
$result['posts_skipped']++;
}
if ($yoast_canonical !== '' && ($overwrite_existing || $existing_canonical === '')) {
update_post_meta($post_id, self::CANONICAL_KEY, esc_url_raw($yoast_canonical));
$result['posts_imported']++;
}
elseif ($yoast_canonical !== '') {
$result['posts_skipped']++;
}
if ($yoast_social_image !== '' && ($overwrite_existing || $existing_social_image === '')) {
update_post_meta($post_id, self::SOCIAL_IMAGE_KEY, esc_url_raw($yoast_social_image));
$result['posts_imported']++;
}
elseif ($yoast_social_image !== '') {
$result['posts_skipped']++;
}
}
}
$yoast_taxonomy_meta = get_option('wpseo_taxonomy_meta', []);
if (is_array($yoast_taxonomy_meta) && !empty($yoast_taxonomy_meta)) {
$public_taxonomies = $this->get_public_taxonomies();
foreach ($public_taxonomies as $taxonomy => $taxonomy_obj) {
if (empty($yoast_taxonomy_meta[$taxonomy]) || !is_array($yoast_taxonomy_meta[$taxonomy])) {
continue;
}
foreach ($yoast_taxonomy_meta[$taxonomy] as $term_id => $term_meta) {
$term_id = (int) $term_id;
if ($term_id <= 0 || !is_array($term_meta)) {
continue;
}
$term = get_term($term_id, $taxonomy);
if (!$term || is_wp_error($term)) {
continue;
}
$yoast_title = isset($term_meta['wpseo_title']) ? (string) $term_meta['wpseo_title'] : '';
$yoast_description = isset($term_meta['wpseo_desc']) ? (string) $term_meta['wpseo_desc'] : '';
$yoast_canonical = isset($term_meta['wpseo_canonical']) ? (string) $term_meta['wpseo_canonical'] : '';
$yoast_social_image = isset($term_meta['wpseo_opengraph-image']) ? (string) $term_meta['wpseo_opengraph-image'] : '';
if ($yoast_social_image === '' && isset($term_meta['wpseo_opengraph-image-url'])) {
$yoast_social_image = (string) $term_meta['wpseo_opengraph-image-url'];
}
if ($yoast_title === '' && $yoast_description === '' && $yoast_canonical === '' && $yoast_social_image === '') {
continue;
}
$existing_title = get_term_meta($term_id, self::META_TITLE_KEY, true);
$existing_description = get_term_meta($term_id, self::META_DESC_KEY, true);
$existing_canonical = get_term_meta($term_id, self::CANONICAL_KEY, true);
$existing_social_image = get_term_meta($term_id, self::SOCIAL_IMAGE_KEY, true);
if ($yoast_title !== '' && ($overwrite_existing || $existing_title === '')) {
update_term_meta($term_id, self::META_TITLE_KEY, sanitize_text_field($yoast_title));
$result['terms_imported']++;
}
elseif ($yoast_title !== '') {
$result['terms_skipped']++;
}
if ($yoast_description !== '' && ($overwrite_existing || $existing_description === '')) {
update_term_meta($term_id, self::META_DESC_KEY, sanitize_textarea_field($yoast_description));
$result['terms_imported']++;
}
elseif ($yoast_description !== '') {
$result['terms_skipped']++;
}
if ($yoast_canonical !== '' && ($overwrite_existing || $existing_canonical === '')) {
update_term_meta($term_id, self::CANONICAL_KEY, esc_url_raw($yoast_canonical));
$result['terms_imported']++;
}
elseif ($yoast_canonical !== '') {
$result['terms_skipped']++;
}
if ($yoast_social_image !== '' && ($overwrite_existing || $existing_social_image === '')) {
update_term_meta($term_id, self::SOCIAL_IMAGE_KEY, esc_url_raw($yoast_social_image));
$result['terms_imported']++;
}
elseif ($yoast_social_image !== '') {
$result['terms_skipped']++;
}
}
}
}
if ($result['posts_imported'] === 0 && $result['terms_imported'] === 0 && $result['settings_imported'] === 0) {
$result['type'] = 'warning';
$result['message'] = __('No Yoast SEO values were imported.', 'ansico-wp-basic');
}
return $result;
}
public function maybe_handle_generate_missing_meta() {
if (!is_admin() || !current_user_can('manage_options')) {
return;
}
if (!isset($_GET['page'], $_GET['action']) || $_GET['page'] !== 'ansico-wp-basic' || $_GET['action'] !== self::GENERATE_ACTION) {
return;
}
check_admin_referer(self::GENERATE_ACTION, 'ansico_wp_basic_generate_nonce');
$result = $this->generate_missing_meta_data();
$redirect_url = add_query_arg([
'page' => 'ansico-wp-basic',
'tab' => 'tools',
'ansico_wp_basic_notice' => rawurlencode(wp_json_encode($result)),
], admin_url('admin.php'));
wp_safe_redirect($redirect_url);
exit;
}
private function generate_missing_meta_data() {
$settings = $this->get_settings();
$post_types = !empty($settings['enabled_post_types']) ? array_values(array_filter(array_map('sanitize_key', (array) $settings['enabled_post_types']))) : [];
$result = [
'type' => 'success',
'message' => __('Missing META values were generated.', 'ansico-wp-basic'),
'posts_imported' => 0,
'terms_imported' => 0,
'posts_skipped' => 0,
'terms_skipped' => 0,
'settings_imported' => 0,
];
if (empty($settings['enable_meta_module']) || empty($post_types)) {
$result['type'] = 'warning';
$result['message'] = __('No enabled post types were available for META generation.', 'ansico-wp-basic');
return $result;
}
$posts = get_posts([
'post_type' => $post_types,
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
'suppress_filters' => true,
]);
foreach ((array) $posts as $post_id) {
$post = get_post($post_id);
if (!$post || empty($post->post_type)) {
continue;
}
$existing_title = get_post_meta($post_id, self::META_TITLE_KEY, true);
$existing_description = get_post_meta($post_id, self::META_DESC_KEY, true);
$updated = false;
if ($existing_title === '') {
$generated_title = sanitize_text_field(get_the_title($post));
if ($generated_title !== '') {
update_post_meta($post_id, self::META_TITLE_KEY, $generated_title);
$result['posts_imported']++;
$updated = true;
}
}
if ($existing_description === '') {
$generated_description = $this->get_default_meta_description_for_post($post);
if ($generated_description !== '') {
update_post_meta($post_id, self::META_DESC_KEY, sanitize_textarea_field($generated_description));
$result['posts_imported']++;
$updated = true;
}
}
if (!$updated) {
$result['posts_skipped']++;
}
}
if ($result['posts_imported'] === 0) {
$result['type'] = 'warning';
$result['message'] = __('No missing META values needed to be generated.', 'ansico-wp-basic');
}
return $result;
}
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(
'
',
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_import_notice() {
if (!is_admin() || !current_user_can('manage_options') || empty($_GET['ansico_wp_basic_notice'])) {
return;
}
$payload = json_decode(wp_unslash($_GET['ansico_wp_basic_notice']), true);
if (!is_array($payload)) {
return;
}
$type = (!empty($payload['type']) && in_array($payload['type'], ['success', 'warning', 'error'], true)) ? $payload['type'] : 'success';
$notice_class = $type === 'error' ? 'notice notice-error' : ($type === 'warning' ? 'notice notice-warning' : 'notice notice-success');
$message = isset($payload['message']) ? (string) $payload['message'] : __('Done.', 'ansico-wp-basic');
$posts_imported = isset($payload['posts_imported']) ? (int) $payload['posts_imported'] : 0;
$terms_imported = isset($payload['terms_imported']) ? (int) $payload['terms_imported'] : 0;
$posts_skipped = isset($payload['posts_skipped']) ? (int) $payload['posts_skipped'] : 0;
$terms_skipped = isset($payload['terms_skipped']) ? (int) $payload['terms_skipped'] : 0;
$settings_imported = isset($payload['settings_imported']) ? (int) $payload['settings_imported'] : 0;
echo '';
echo esc_html($message);
echo ' ';
echo esc_html(sprintf(__('Imported post fields: %1$d. Imported term fields: %2$d. Skipped existing post fields: %3$d. Skipped existing term fields: %4$d. Imported site settings: %5$d.', 'ansico-wp-basic'), $posts_imported, $terms_imported, $posts_skipped, $terms_skipped, $settings_imported));
echo '
';
}
private function get_current_settings_tab() {
$allowed_tabs = ['general', 'archives', 'tools'];
$tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : 'general';
return in_array($tab, $allowed_tabs, true) ? $tab : 'general';
}
public function render_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
$settings = $this->get_settings();
$current_tab = $this->get_current_settings_tab();
$base_url = admin_url('admin.php?page=ansico-wp-basic');
$tabs = [
'general' => __('General settings', 'ansico-wp-basic'),
'archives' => __('Archives and special pages', 'ansico-wp-basic'),
'tools' => __('Tools', '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 '';
}
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);
$saved_canonical = get_post_meta($post->ID, self::CANONICAL_KEY, true);
$saved_social_image = get_post_meta($post->ID, self::SOCIAL_IMAGE_KEY, true);
$saved_x_creator = get_post_meta($post->ID, self::X_CREATOR_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,
'canonical' => $saved_canonical,
'social_image' => $saved_social_image,
'x_creator' => $saved_x_creator,
]);
}
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'])) : '';
$canonical = isset($_POST['ansico_wp_basic_canonical']) ? esc_url_raw(wp_unslash($_POST['ansico_wp_basic_canonical'])) : '';
$social_image = isset($_POST['ansico_wp_basic_social_image']) ? esc_url_raw(wp_unslash($_POST['ansico_wp_basic_social_image'])) : '';
$x_creator = isset($_POST['ansico_wp_basic_x_creator']) ? sanitize_text_field(wp_unslash($_POST['ansico_wp_basic_x_creator'])) : '';
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);
}
if ($canonical === '') {
delete_post_meta($post_id, self::CANONICAL_KEY);
} else {
update_post_meta($post_id, self::CANONICAL_KEY, $canonical);
}
if ($social_image === '') {
delete_post_meta($post_id, self::SOCIAL_IMAGE_KEY);
} else {
update_post_meta($post_id, self::SOCIAL_IMAGE_KEY, $social_image);
}
if ($x_creator === '') {
delete_post_meta($post_id, self::X_CREATOR_KEY);
} else {
update_post_meta($post_id, self::X_CREATOR_KEY, $this->normalize_x_handle($x_creator));
}
}
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,
'canonical' => '',
'social_image' => '',
'x_creator' => '',
]); ?>
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);
$canonical = get_term_meta($term->term_id, self::CANONICAL_KEY, true);
$social_image = get_term_meta($term->term_id, self::SOCIAL_IMAGE_KEY, true);
$x_creator = get_term_meta($term->term_id, self::X_CREATOR_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('/')),
'canonical' => $canonical,
'social_image' => $social_image,
'x_creator' => $x_creator,
]); ?>
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'])) : '';
$canonical = isset($_POST['ansico_wp_basic_canonical']) ? esc_url_raw(wp_unslash($_POST['ansico_wp_basic_canonical'])) : '';
$social_image = isset($_POST['ansico_wp_basic_social_image']) ? esc_url_raw(wp_unslash($_POST['ansico_wp_basic_social_image'])) : '';
$x_creator = isset($_POST['ansico_wp_basic_x_creator']) ? sanitize_text_field(wp_unslash($_POST['ansico_wp_basic_x_creator'])) : '';
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);
}
if ($canonical === '') {
delete_term_meta($term_id, self::CANONICAL_KEY);
} else {
update_term_meta($term_id, self::CANONICAL_KEY, $canonical);
}
if ($social_image === '') {
delete_term_meta($term_id, self::SOCIAL_IMAGE_KEY);
} else {
update_term_meta($term_id, self::SOCIAL_IMAGE_KEY, $social_image);
}
if ($x_creator === '') {
delete_term_meta($term_id, self::X_CREATOR_KEY);
} else {
update_term_meta($term_id, self::X_CREATOR_KEY, $this->normalize_x_handle($x_creator));
}
}
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.1.4'
);
wp_enqueue_script(
'ansico-wp-basic-admin',
plugin_dir_url(__FILE__) . 'assets/admin.js',
[],
'0.0.1.4',
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 maybe_disable_core_canonical() {
$settings = $this->get_settings();
if (!empty($settings['enable_meta_module'])) {
remove_action('wp_head', 'rel_canonical');
}
}
private function get_context_url() {
if (is_singular()) {
$post_id = get_queried_object_id();
if ($post_id) {
return get_permalink($post_id) ?: home_url('/');
}
}
if (is_author()) {
$author = get_queried_object();
if ($author instanceof WP_User || (is_object($author) && isset($author->ID))) {
return get_author_posts_url((int) $author->ID);
}
}
if ((is_category() || is_tag() || is_tax()) && ($term = get_queried_object()) && (isset($term->term_id))) {
$link = get_term_link($term);
return !is_wp_error($link) ? $link : home_url('/');
}
if (is_post_type_archive()) {
$post_type = get_query_var('post_type');
$post_type = is_array($post_type) ? reset($post_type) : $post_type;
return $post_type ? get_post_type_archive_link($post_type) : home_url('/');
}
if (is_home()) {
$page_for_posts = (int) get_option('page_for_posts');
if ($page_for_posts) {
return get_permalink($page_for_posts) ?: home_url('/');
}
}
global $wp;
if (isset($wp->request)) {
return home_url(add_query_arg([], $wp->request));
}
return home_url('/');
}
private function get_canonical_url() {
if (is_singular()) {
$post_id = get_queried_object_id();
if ($post_id) {
$override = get_post_meta($post_id, self::CANONICAL_KEY, true);
if (!empty($override)) {
return esc_url_raw($override);
}
}
$canonical = wp_get_canonical_url();
if (!empty($canonical)) {
return $canonical;
}
}
return $this->get_context_url();
}
private function get_og_locale() {
$locale = get_locale();
return str_replace('-', '_', (string) $locale);
}
private function get_og_type() {
if (is_singular() && !is_page()) {
return 'article';
}
return 'website';
}
private function normalize_x_handle($handle) {
$handle = trim((string) $handle);
if ($handle === '') {
return '';
}
return strpos($handle, '@') === 0 ? $handle : '@' . ltrim($handle, '@');
}
private function get_author_name_for_context() {
if (is_singular()) {
$post = get_queried_object();
if ($post instanceof WP_Post) {
return get_the_author_meta('display_name', (int) $post->post_author);
}
}
return '';
}
private function get_author_url_for_context() {
if (is_singular()) {
$post = get_queried_object();
if ($post instanceof WP_Post) {
return get_author_posts_url((int) $post->post_author);
}
}
return '';
}
private function get_social_image_data() {
$settings = $this->get_settings();
if (is_singular()) {
$post_id = get_queried_object_id();
if ($post_id) {
$override_url = get_post_meta($post_id, self::SOCIAL_IMAGE_KEY, true);
if (!empty($override_url)) {
return [
'url' => esc_url_raw($override_url),
'width' => 0,
'height' => 0,
'type' => '',
];
}
}
$thumb_id = $post_id ? get_post_thumbnail_id($post_id) : 0;
if ($thumb_id) {
$src = wp_get_attachment_image_src($thumb_id, 'full');
if ($src) {
$file = get_attached_file($thumb_id);
$mime = $file && function_exists('wp_check_filetype') ? wp_check_filetype($file) : ['type' => ''];
return [
'url' => $src[0],
'width' => (int) $src[1],
'height' => (int) $src[2],
'type' => !empty($mime['type']) ? $mime['type'] : '',
];
}
}
}
if (is_tax() || is_category() || is_tag()) {
$term = get_queried_object();
if ($term instanceof WP_Term) {
$override_url = get_term_meta($term->term_id, self::SOCIAL_IMAGE_KEY, true);
if (!empty($override_url)) {
return [
'url' => esc_url_raw($override_url),
'width' => 0,
'height' => 0,
'type' => '',
];
}
}
}
if (!empty($settings['social_defaults']['default_social_image'])) {
return [
'url' => esc_url_raw($settings['social_defaults']['default_social_image']),
'width' => 0,
'height' => 0,
'type' => '',
];
}
$icon_id = (int) get_option('site_icon');
if ($icon_id) {
$src = wp_get_attachment_image_src($icon_id, 'full');
if ($src) {
$file = get_attached_file($icon_id);
$mime = $file && function_exists('wp_check_filetype') ? wp_check_filetype($file) : ['type' => ''];
return [
'url' => $src[0],
'width' => (int) $src[1],
'height' => (int) $src[2],
'type' => !empty($mime['type']) ? $mime['type'] : '',
];
}
}
return [];
}
private function get_reading_time_label($post) {
if (!($post instanceof WP_Post)) {
return '';
}
$word_count = str_word_count(wp_strip_all_tags((string) $post->post_content));
if ($word_count <= 0) {
return '';
}
$minutes = max(1, (int) ceil($word_count / 200));
return sprintf(_n('%d minute', '%d minutes', $minutes, 'ansico-wp-basic'), $minutes);
}
private function get_x_creator_for_context($fallback = '') {
if (is_singular()) {
$post_id = get_queried_object_id();
if ($post_id) {
$override = get_post_meta($post_id, self::X_CREATOR_KEY, true);
if (!empty($override)) {
return $this->normalize_x_handle($override);
}
}
}
if (is_tax() || is_category() || is_tag()) {
$term = get_queried_object();
if ($term instanceof WP_Term) {
$override = get_term_meta($term->term_id, self::X_CREATOR_KEY, true);
if (!empty($override)) {
return $this->normalize_x_handle($override);
}
}
}
return $this->normalize_x_handle($fallback);
}
public function output_frontend_meta_tags() {
if (is_admin() || is_feed()) {
return;
}
$settings = $this->get_settings();
if (empty($settings['enable_meta_module'])) {
return;
}
$canonical = $this->get_canonical_url();
$title = $this->get_context_meta_title();
if ($title === '') {
$title = wp_get_document_title();
} else {
$title = $this->build_document_title_from_meta($title);
}
$description = $this->get_context_meta_description();
$url = $this->get_context_url();
$site_name = get_bloginfo('name');
$og_locale = $this->get_og_locale();
$og_type = $this->get_og_type();
$social = $settings['social_defaults'] ?? [];
$facebook_publisher = !empty($social['facebook_publisher_url']) ? esc_url($social['facebook_publisher_url']) : '';
$x_site_handle = $this->normalize_x_handle($social['x_site_handle'] ?? '');
$x_creator_handle = $this->get_x_creator_for_context($x_site_handle);
$author_name = $this->get_author_name_for_context();
$author_url = $this->get_author_url_for_context();
$image = $this->get_social_image_data();
$twitter_card = !empty($image['url']) ? 'summary_large_image' : 'summary';
echo "
" . ' ' . "
";
echo ' ' . "
";
echo ' ' . "
";
echo ' ' . "
";
if ($description !== '') {
echo ' ' . "
";
}
echo ' ' . "
";
echo ' ' . "
";
if ($facebook_publisher !== '') {
echo ' ' . "
";
}
if ($author_url !== '') {
echo ' ' . "
";
}
if (is_singular()) {
$post = get_queried_object();
if ($post instanceof WP_Post) {
if (!empty($post->post_date_gmt)) {
echo ' ' . "
";
}
if (!empty($post->post_modified_gmt)) {
echo ' ' . "
";
}
}
}
if (!empty($image['url'])) {
echo ' ' . "
";
if (!empty($image['width'])) {
echo ' ' . "
";
}
if (!empty($image['height'])) {
echo ' ' . "
";
}
if (!empty($image['type'])) {
echo ' ' . "
";
}
}
if ($author_name !== '') {
echo ' ' . "
";
}
echo ' ' . "
";
if ($x_creator_handle !== '') {
echo ' ' . "
";
}
if ($x_site_handle !== '') {
echo ' ' . "
";
}
if ($author_name !== '') {
echo ' ' . "
";
echo ' ' . "
";
}
if (is_singular()) {
$post = get_queried_object();
$reading_time = $this->get_reading_time_label($post);
if ($reading_time !== '') {
echo ' ' . "
";
echo ' ' . "
";
}
}
$organization_name = !empty($social['organization_name']) ? $social['organization_name'] : $site_name;
$organization_logo = !empty($social['organization_logo']) ? esc_url_raw($social['organization_logo']) : '';
$schema_graph = [
[
'@type' => 'WebSite',
'@id' => trailingslashit(home_url('/')) . '#website',
'url' => home_url('/'),
'name' => $site_name,
'publisher' => ['@id' => trailingslashit(home_url('/')) . '#organization'],
'inLanguage' => str_replace('_', '-', $og_locale),
],
[
'@type' => 'Organization',
'@id' => trailingslashit(home_url('/')) . '#organization',
'name' => $organization_name,
'url' => home_url('/'),
'sameAs' => array_values(array_filter([$facebook_publisher])),
'logo' => $organization_logo !== '' ? [
'@type' => 'ImageObject',
'url' => $organization_logo,
] : null,
],
[
'@type' => is_singular() && !is_page() ? 'Article' : 'WebPage',
'@id' => trailingslashit($url) . '#webpage',
'url' => $url,
'name' => wp_strip_all_tags($title),
'isPartOf' => ['@id' => trailingslashit(home_url('/')) . '#website'],
'description' => $description,
'inLanguage' => str_replace('_', '-', $og_locale),
],
];
if ($author_name !== '') {
$schema_graph[2]['author'] = [
'@type' => 'Person',
'name' => $author_name,
'url' => $author_url,
];
}
if (!empty($image['url'])) {
$schema_graph[] = [
'@type' => 'ImageObject',
'@id' => trailingslashit($url) . '#primaryimage',
'url' => $image['url'],
'contentUrl' => $image['url'],
'width' => !empty($image['width']) ? (int) $image['width'] : null,
'height' => !empty($image['height']) ? (int) $image['height'] : null,
];
$schema_graph[2]['image'] = ['@id' => trailingslashit($url) . '#primaryimage'];
}
if (is_singular()) {
$post = get_queried_object();
if ($post instanceof WP_Post) {
$schema_graph[1]['datePublished'] = get_post_time('c', true, $post);
$schema_graph[1]['dateModified'] = get_post_modified_time('c', true, $post);
}
}
$schema_graph = array_map(function($item) {
return array_filter($item, function($value) {
return !($value === '' || $value === null || $value === []);
});
}, $schema_graph);
echo '' . "
";
}
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();