From fcaeed98eb750f43833a545ba7681722137743ef Mon Sep 17 00:00:00 2001 From: Andreas Date: Fri, 17 Apr 2026 23:07:12 +0200 Subject: [PATCH] Synced folder --- ansico-wp-basic-1.0.0-final.zip | Bin 0 -> 22662 bytes ...o-wp-basic-1.0.0-final.zip:Zone.Identifier | Bin 0 -> 25 bytes ansico-wp-basic-v0.0.0.6.zip | Bin 12646 -> 0 bytes ansico-wp-basic/ansico-wp-basic.php | 1136 ++++++++++++++++- ansico-wp-basic/assets/admin.css | 281 +++- ansico-wp-basic/assets/admin.js | 58 +- ansico-wp-basic/readme.txt | 77 +- 7 files changed, 1496 insertions(+), 56 deletions(-) create mode 100644 ansico-wp-basic-1.0.0-final.zip create mode 100644 ansico-wp-basic-1.0.0-final.zip:Zone.Identifier delete mode 100644 ansico-wp-basic-v0.0.0.6.zip diff --git a/ansico-wp-basic-1.0.0-final.zip b/ansico-wp-basic-1.0.0-final.zip new file mode 100644 index 0000000000000000000000000000000000000000..3f70f76069402fdf4cfc81626f5d57a0208407fe GIT binary patch literal 22662 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 $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' => ''], @@ -95,6 +111,7 @@ final class Ansico_WP_Basic { $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']); @@ -133,6 +150,18 @@ final class Ansico_WP_Basic { 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' => ''], @@ -247,6 +276,23 @@ final class 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'), @@ -271,6 +317,31 @@ final class Ansico_WP_Basic { '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) { @@ -292,6 +363,7 @@ final class Ansico_WP_Basic { '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'] : []), ]; @@ -311,23 +383,35 @@ final class Ansico_WP_Basic { public function render_sitemap_module_toggle() { $settings = $this->get_settings(); + $sitemap_url = home_url('/sitemap.xml'); 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') + 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 '

' . 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 '
'; + 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), @@ -335,6 +419,7 @@ final class Ansico_WP_Basic { ); } 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() { @@ -359,6 +444,20 @@ final class Ansico_WP_Basic { ); } + public function render_social_defaults_fields() { + $settings = $this->get_settings(); + $social = $settings['social_defaults'] ?? []; + + echo '
'; + echo '

'; + echo '

'; + echo '

'; + echo '

'; + echo '

'; + echo '

' . esc_html__('Used for Open Graph, Twitter/X, canonical tags, and lightweight schema output. These values can also be imported from Yoast SEO when available.', 'ansico-wp-basic') . '

'; + echo '
'; + } + public function render_special_pages_fields() { $settings = $this->get_settings(); if (empty($settings['enable_meta_module'])) { @@ -392,6 +491,394 @@ final class Ansico_WP_Basic { 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 '
'; + echo '

' . esc_html__('Copies filled Yoast SEO meta title, meta description, canonical URL, social image, and selected site-wide social settings into Ansico WP Basic. Existing Ansico values are kept unless you explicitly overwrite them.', 'ansico-wp-basic') . '

'; + echo '

' . esc_html__('Import from Yoast SEO', 'ansico-wp-basic') . '

'; + echo '

' . esc_html__('Safe to run multiple times. This importer currently brings over title, description, canonical URL, Open Graph image, and selected Yoast social defaults.', 'ansico-wp-basic') . '

'; + 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 '
'; + echo '

' . esc_html__('Generate missing META title and META description values for existing posts where one or both fields are empty. Existing Ansico values are kept.', 'ansico-wp-basic') . '

'; + if (!empty($labels)) { + echo '

' . esc_html(sprintf(__('Runs on enabled post types: %s.', 'ansico-wp-basic'), implode(', ', $labels))) . '

'; + } + echo '

' . esc_html__('Generate missing META fields', 'ansico-wp-basic') . '

'; + echo '

' . esc_html__('META title is generated from the post title. META description uses the first paragraph of the content, trimmed to up to 150 characters.', 'ansico-wp-basic') . '

'; + 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'])) { @@ -425,28 +912,153 @@ final class Ansico_WP_Basic { 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(); ?> -

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

-
- -
+
+
+

+

+
+
+ + + + +
+
+
+

+
+
+
+

+ render_yoast_import_field(); ?> +
+
+

+ render_generate_missing_meta_field(); ?> +
+
+
+
+ +
+ + +
+ +
+
+

+
+
+
+

+
render_meta_module_toggle(); ?>
+
+
+

+
render_sitemap_module_toggle(); ?>
+
+
+

+
render_post_types_field(); ?>
+
+
+

+
render_author_field_toggle(); ?>
+
+
+

+
render_taxonomy_field_toggle(); ?>
+
+
+
+ +
+
+

+

+
+ render_social_defaults_fields(); ?> +
+ +
+
+

+
+
+

+ render_special_pages_fields(); ?> +
+
+

+ render_post_type_archive_fields(); ?> +
+
+ +
+ +
+ 'button button-primary button-large']); ?> +
+
+
get_settings(); foreach ($settings['enabled_post_types'] as $post_type) { if (!empty($settings['enable_meta_module'])) { @@ -518,6 +1130,22 @@ final class Ansico_WP_Basic {

+

+ + + +

+

+ + + +

+

+ + + +

+
@@ -629,6 +1257,9 @@ final class Ansico_WP_Basic { $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); @@ -639,6 +1270,9 @@ final class Ansico_WP_Basic { 'meta_description' => $meta_description, 'fallback_title' => $fallback_title, 'permalink' => $permalink, + 'canonical' => $saved_canonical, + 'social_image' => $saved_social_image, + 'x_creator' => $saved_x_creator, ]); } @@ -676,6 +1310,9 @@ final class Ansico_WP_Basic { $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); @@ -688,6 +1325,24 @@ final class Ansico_WP_Basic { } 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) { @@ -757,6 +1412,9 @@ final class Ansico_WP_Basic { 'meta_description' => '', 'fallback_title' => ucfirst($taxonomy_name) . ' archive', 'permalink' => $example_url, + 'canonical' => '', + 'social_image' => '', + 'x_creator' => '', ]); ?>
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); ?> @@ -780,6 +1441,9 @@ final class Ansico_WP_Basic { '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, ]); ?> @@ -808,6 +1472,9 @@ final class Ansico_WP_Basic { $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); @@ -820,6 +1487,24 @@ final class Ansico_WP_Basic { } 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) { @@ -891,14 +1576,14 @@ final class Ansico_WP_Basic { 'ansico-wp-basic-admin', plugin_dir_url(__FILE__) . 'assets/admin.css', [], - '0.0.0.2' + '0.0.1.4' ); wp_enqueue_script( 'ansico-wp-basic-admin', plugin_dir_url(__FILE__) . 'assets/admin.js', [], - '0.0.0.2', + '0.0.1.4', true ); } @@ -1009,6 +1694,421 @@ final class Ansico_WP_Basic { 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; diff --git a/ansico-wp-basic/assets/admin.css b/ansico-wp-basic/assets/admin.css index 65edd61..9bcc926 100644 --- a/ansico-wp-basic/assets/admin.css +++ b/ansico-wp-basic/assets/admin.css @@ -74,8 +74,155 @@ font-weight: 600; } -.ansico-wp-basic-settings-page .form-table th { - width: 280px; +.ansico-wp-basic-settings-page { + max-width: 1320px; +} + +.ansico-wp-basic-page-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 20px; + margin: 22px 0 20px; +} + +.ansico-wp-basic-page-header h1 { + margin: 0 0 8px; +} + +.ansico-wp-basic-page-intro { + margin: 0; + color: #50575e; + max-width: 760px; +} + +.ansico-wp-basic-status-card, +.ansico-wp-basic-panel, +.ansico-wp-basic-settings-card, +.ansico-wp-basic-tool-card { + background: #fff; + border: 1px solid #dcdcde; + border-radius: 12px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); +} + +.ansico-wp-basic-status-card { + min-width: 260px; + padding: 16px 18px; +} + +.ansico-wp-basic-status-label { + display: block; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.03em; + text-transform: uppercase; + color: #50575e; + margin-bottom: 8px; +} + +.ansico-wp-basic-status-muted { + color: #50575e; +} + +.ansico-wp-basic-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 340px; + gap: 20px; + align-items: start; +} + +.ansico-wp-basic-main-column, +.ansico-wp-basic-side-column, +.ansico-wp-basic-tool-stack { + display: grid; + gap: 20px; +} + +.ansico-wp-basic-panel { + padding: 22px; +} + +.ansico-wp-basic-panel-sticky { + position: sticky; + top: 42px; +} + +.ansico-wp-basic-panel-header { + margin-bottom: 18px; + padding-bottom: 16px; + border-bottom: 1px solid #f0f0f1; +} + +.ansico-wp-basic-panel-header h2 { + margin: 0 0 6px; + font-size: 20px; + line-height: 1.3; +} + +.ansico-wp-basic-panel-header p, +.ansico-wp-basic-subsection > h3 + p { + margin: 0; + color: #50575e; +} + +.ansico-wp-basic-field-list { + display: grid; + gap: 0; +} + +.ansico-wp-basic-field-row { + display: grid; + grid-template-columns: minmax(180px, 240px) minmax(0, 1fr); + gap: 24px; + padding: 18px 0; + border-top: 1px solid #f0f0f1; +} + +.ansico-wp-basic-field-row:first-child { + border-top: 0; + padding-top: 0; +} + +.ansico-wp-basic-field-row:last-child { + padding-bottom: 0; +} + +.ansico-wp-basic-field-label h3, +.ansico-wp-basic-subsection > h3, +.ansico-wp-basic-tool-card h3, +.ansico-wp-basic-settings-card h3 { + margin: 0; + font-size: 15px; + line-height: 1.4; +} + +.ansico-wp-basic-field-control .description, +.ansico-wp-basic-settings-card .description, +.ansico-wp-basic-tool-card .description, +.ansico-wp-basic-tools-box .description { + color: #646970; +} + +.ansico-wp-basic-checkbox-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 10px; + margin: 0; +} + +.ansico-wp-basic-checkbox-item { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 12px 14px; + border: 1px solid #e0e0e0; + border-radius: 10px; + background: #fcfcfc; +} + +.ansico-wp-basic-checkbox-item input { + margin-top: 2px; } .ansico-wp-basic-settings-grid { @@ -85,24 +232,42 @@ } .ansico-wp-basic-settings-card { - background: #fff; - border: 1px solid #dcdcde; - border-radius: 8px; - padding: 14px; + padding: 16px; } .ansico-wp-basic-settings-card h3 { + margin-bottom: 14px; +} + +.ansico-wp-basic-subsection { + margin-top: 22px; +} + +.ansico-wp-basic-subsection:first-of-type { margin-top: 0; } +.ansico-wp-basic-subsection > h3 { + margin-bottom: 14px; +} + .ansico-wp-basic-term-fields .ansico-wp-basic-metabox, .ansico-wp-basic-term-fields-wrap .ansico-wp-basic-metabox { background: #fff; } +.ansico-wp-basic-tool-card { + padding: 18px; +} +.ansico-wp-basic-tools-box, .ansico-wp-basic-tools-box p { - margin: 0 0 14px; + margin: 0; +} + +.ansico-wp-basic-tools-box { + display: grid; + gap: 12px; } .ansico-wp-basic-tools-box label strong { @@ -119,7 +284,103 @@ vertical-align: middle; } -.ansico-wp-basic-tools-box .description { - display: block; - margin-top: 6px; +.ansico-wp-basic-submit-row { + margin-top: 20px; +} + +@media (max-width: 1100px) { + .ansico-wp-basic-layout { + grid-template-columns: 1fr; + } + + .ansico-wp-basic-panel-sticky { + position: static; + } +} + +@media (max-width: 782px) { + .ansico-wp-basic-page-header, + .ansico-wp-basic-field-row { + grid-template-columns: 1fr; + display: grid; + } + + .ansico-wp-basic-page-header { + gap: 14px; + } + + .ansico-wp-basic-panel, + .ansico-wp-basic-tool-card, + .ansico-wp-basic-settings-card, + .ansico-wp-basic-status-card { + padding: 16px; + } +} + + +.ansico-wp-basic-tab-nav { + margin: 0 0 20px; +} + +.ansico-wp-basic-tab-panel { + display: grid; + gap: 20px; +} + +.ansico-wp-basic-panel-narrow { + max-width: 900px; +} + + +.ansico-wp-basic-panel-header-intro { + margin-bottom: 8px; +} + +.ansico-wp-basic-panel-header-intro p { + font-size: 14px; +} + +.ansico-wp-basic-inline-code { + margin: 12px 0 0; +} + +.ansico-wp-basic-inline-code code { + word-break: break-all; +} + + +.ansico-wp-basic-sitemap-url-row { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + margin-top: 12px; +} + +.ansico-wp-basic-sitemap-url-label { + margin-right: 2px; +} + +.ansico-wp-basic-sitemap-url { + display: inline-block; + padding: 6px 8px; +} + +.ansico-wp-basic-copy-button { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 34px; + padding: 0 6px; +} + +.ansico-wp-basic-copy-button .dashicons { + font-size: 16px; + width: 16px; + height: 16px; +} + +.ansico-wp-basic-copy-button.is-copied { + color: #008a20; + border-color: #8bc34a; } diff --git a/ansico-wp-basic/assets/admin.js b/ansico-wp-basic/assets/admin.js index 33c42d8..1aca09d 100644 --- a/ansico-wp-basic/assets/admin.js +++ b/ansico-wp-basic/assets/admin.js @@ -3,9 +3,7 @@ document.addEventListener('DOMContentLoaded', function () { var descInput = document.getElementById('ansico_wp_basic_meta_description'); var snippet = document.querySelector('.ansico-wp-basic-snippet'); - if (!titleInput || !descInput || !snippet) { - return; - } + if (titleInput && descInput && snippet) { var titleTarget = snippet.querySelector('.ansico-wp-basic-snippet-title'); var descTextTarget = snippet.querySelector('.ansico-wp-basic-snippet-description-text'); @@ -157,4 +155,58 @@ document.addEventListener('DOMContentLoaded', function () { titleInput.addEventListener('input', updateSnippet); descInput.addEventListener('input', updateSnippet); updateSnippet(); + } + + + document.querySelectorAll('.ansico-wp-basic-copy-button').forEach(function (button) { + button.addEventListener('click', function () { + var text = button.getAttribute('data-copy-text') || ''; + if (!text) { + return; + } + + var resetButtonState = function () { + button.classList.remove('is-copied'); + button.setAttribute('title', button.getAttribute('data-original-title') || ''); + button.setAttribute('aria-label', button.getAttribute('data-original-label') || ''); + }; + + if (!button.hasAttribute('data-original-title')) { + button.setAttribute('data-original-title', button.getAttribute('title') || ''); + } + + if (!button.hasAttribute('data-original-label')) { + button.setAttribute('data-original-label', button.getAttribute('aria-label') || ''); + } + + var markCopied = function () { + button.classList.add('is-copied'); + button.setAttribute('title', 'Copied'); + button.setAttribute('aria-label', 'Copied'); + window.setTimeout(resetButtonState, 1500); + }; + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(markCopied).catch(function () { + var tempInput = document.createElement('input'); + tempInput.value = text; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand('copy'); + document.body.removeChild(tempInput); + markCopied(); + }); + return; + } + + var tempInput = document.createElement('input'); + tempInput.value = text; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand('copy'); + document.body.removeChild(tempInput); + markCopied(); + }); + }); + }); diff --git a/ansico-wp-basic/readme.txt b/ansico-wp-basic/readme.txt index afad00a..4c138b0 100644 --- a/ansico-wp-basic/readme.txt +++ b/ansico-wp-basic/readme.txt @@ -1,43 +1,70 @@ === Ansico WP Basic === -Contributors: ansico -Tags: seo, meta title, meta description, search preview +Contributors: aphandersen +Tags: seo, metadata, meta tags, open graph, xml sitemap Requires at least: 6.0 -Tested up to: 6.5 +Tested up to: 6.9.4 Requires PHP: 7.4 -Stable tag: 0.0.0.2 +Stable tag: 1.0.0 License: GPLv3 or later License URI: https://www.gnu.org/licenses/gpl-3.0.html -Basic SEO fields for posts, pages, custom post types, author archives, taxonomy archives, and special archive pages. +Lightweight SEO tools for WordPress with meta titles, meta descriptions, social tags, canonical URLs, and XML sitemaps. == Description == -Ansico WP Basic adds simple SEO fields to WordPress: +Ansico WP Basic adds essential SEO controls to WordPress without the overhead of a large SEO suite. -- Meta title and meta description fields for posts, pages, and public custom post types -- Live search result preview in the editor -- SEO fields for author archives via user profiles -- SEO fields for taxonomy archives such as categories, tags, and public custom taxonomies -- Settings for archive-style pages such as blog home, date archives, search results, 404, and post type archives -- Outputs the meta description tag in the frontend head when a description is available -- Uses the custom meta title as the document title when one is available +Features include: + +* Meta title and meta description fields for posts, pages, and public custom post types +* Live search result preview in the editor +* SEO fields for author archives +* SEO fields for taxonomy archives such as categories, tags, and public custom taxonomies +* SEO settings for archive-style pages such as the posts page, date archives, search results, 404 pages, and post type archives +* Open Graph and Twitter/X meta tags +* Canonical URL output +* Lightweight JSON-LD schema output +* XML sitemap generation +* Import tools for migrating SEO data from Yoast SEO +* Bulk generation of missing meta titles and meta descriptions for existing content + +The plugin is designed for site owners who want straightforward SEO fields and metadata management directly inside WordPress. == Installation == -1. Upload the plugin ZIP file in WordPress. -2. Activate the plugin. -3. Go to Ansico WP Basic > Settings. -4. Choose which post types should have SEO fields. +1. Upload the plugin ZIP file through the WordPress admin plugin installer, or upload the plugin folder to `/wp-content/plugins/`. +2. Activate the plugin through the **Plugins** screen in WordPress. +3. Go to **Ansico WP Basic > Settings**. +4. Choose the post types and archive areas where SEO fields should be available. +5. Configure optional social defaults, sitemap settings, and maintenance tools as needed. + +== Frequently Asked Questions == + += Can I import my SEO data from Yoast SEO? = + +Yes. The **Tools** tab includes an importer for supported Yoast SEO metadata and selected social defaults. + += Will existing values be overwritten? = + +No. The importer and bulk generator are designed to preserve existing Ansico WP Basic values unless a field is empty. + += Does the plugin support custom post types? = + +Yes. Public custom post types can be enabled from the settings screen. == Changelog == -= 1.1.0 = -- Improved search result preview styling -- Made meta description output always active -- Removed title and description output toggles from settings -- Added support for author archives -- Added support for taxonomy term archives -- Added settings for archive and special pages += 1.0.0 = +* First public release prepared for WordPress.org +* Added meta title and meta description support for posts, pages, public custom post types, author archives, taxonomy archives, and archive-style pages +* Added live search result preview in the editor +* Added Open Graph, Twitter/X, canonical URL, and lightweight schema output +* Added XML sitemap generation +* Added Yoast SEO import tools for supported metadata and social defaults +* Added bulk generation of missing meta titles and meta descriptions +* Improved the settings screen with tabs and cleaner organization + +== Upgrade Notice == = 1.0.0 = -- Initial release +Initial public release.