' . 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 '' . 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 '' . 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 '
@@ -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.