diff --git a/README.md b/README.md index ed7fee9..6f98513 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,45 @@ -# WP Plugin Auto-Release (Gitea Actions) +# Advanced SEO Meta + AI FAQ Schema -Automatically versions and releases this WordPress plugin whenever the -plugin's `Version:` header changes on `main`. No manual tagging, no -hardcoded plugin name/slug — everything is read straight from the plugin's -own file header. +Advanced SEO Meta + AI FAQ Schema is a WordPress plugin that helps you control how your content appears in search results and AI-powered search experiences. -## What it does +## Features -On every push to `main`, the workflow at -[`.gitea/workflows/wp-plugin-release.yml`](.gitea/workflows/wp-plugin-release.yml) runs: +- Custom SEO title field for posts and pages +- Custom meta description field for better search snippet control +- FAQ builder that lets you add multiple questions and answers +- Automatic JSON-LD FAQPage schema output for AI search engines such as Google SGE and Bing Chat +- Open Graph tags for Facebook and LinkedIn sharing +- Clean meta box in the WordPress editor for easy content optimization -1. **Locate the main plugin file** — scans all `*.php` files for one - containing a `Plugin Name:` header (skips `vendor/`, `node_modules/`, - `build/`). -2. **Extract metadata** — reads `Plugin Name:` and `Version:` from that - file's header comment, then derives a slug from the plugin name - (e.g. `My Cool Plugin` → `my-cool-plugin`). -3. **Check for a new version** — looks for a git tag `vX.Y.Z` matching the - version found in step 2. - - **Tag already exists** → workflow stops here. Pushing commits without - bumping the version does nothing. - - **Tag doesn't exist** → it's a new release, continue. -4. **Sync `readme.txt`** — updates the `Stable tag:` line to match, if the - file exists, and commits that change back to `main`. -5. **Tag** the commit as `vX.Y.Z` and push the tag. -6. **Build a zip** of the plugin (excluding `.git`, `.gitea`, `.github`, - `node_modules`, `build`), named `-.zip`. -7. **Publish a Gitea Release** for the new tag with the zip attached as a - downloadable asset. +## What the plugin adds -## Requirements +When editing a post or page, you can fill in: -- A **Gitea Actions runner** (`act_runner`) registered on your instance. -- The runner image needs `rsync`, `zip`, and `git` available (standard on - `ubuntu-latest`-style runner images). -- A **repo secret** named `RELEASE_TOKEN` — a Gitea access token with - repo write / release permissions. Add it under - **Repo → Settings → Secrets**. +- SEO Title +- Meta Description +- FAQ questions and answers + +The plugin then outputs: + +- A custom page title override +- A meta description tag +- Open Graph meta tags +- Structured FAQ schema in JSON-LD format + +## Installation + +1. Upload the plugin folder to your WordPress installation's plugins directory. +2. Activate the plugin from the WordPress admin panel. +3. Edit any post or page and look for the "Advanced SEO & AI FAQ Schema" meta box. ## Usage -1. Bump the version in your plugin's main file header: +1. Open a post or page in the editor. +2. Enter a custom SEO title and meta description. +3. Add one or more FAQ entries using the built-in builder. +4. Publish or update the post to make the SEO and schema output available on the front end. - ```php - /** - * Plugin Name: My Cool Plugin - * Version: 1.3.0 - */ - ``` +## Notes -2. Commit and push to `main`: +The plugin is designed for posts and pages and works best when used alongside strong on-page content. The FAQ schema is especially useful for improving visibility in AI-driven search experiences. - ```bash - git add . - git commit -m "Release 1.3.0" - git push origin main - ``` - -3. The workflow detects the new version, tags `v1.3.0`, builds - `my-cool-plugin-1.3.0.zip`, and publishes it as a Gitea release — - nothing else to do. - -## Plugin file requirements - -Your main plugin file must use the standard WordPress header format, -with `Plugin Name:` and `Version:` inside the top `/** ... */` comment -block: - -```php -\' + + \'

\' + + \'

\' + + \'\' + + \'\'; + $(this).before(row); + }); + // Remove FAQ row + $(document).on("click", ".remove-faq-row", function(e) { + e.preventDefault(); + $(this).closest(".faq-row").remove(); + }); + }); + ' ); + } + + /** + * Add the main SEO + FAQ meta box. + */ + public function add_meta_box() { + add_meta_box( + 'advanced_seo_meta_box', + __( 'Advanced SEO & AI FAQ Schema', 'advanced-seo-meta' ), + array( $this, 'render_meta_box' ), + ['post', 'page'], + 'normal', + 'default' + ); + } + + /** + * Render the meta box (Title, Description, FAQ builder). + */ + public function render_meta_box( $post ) { + wp_nonce_field( 'advanced_seo_nonce', 'advanced_seo_nonce' ); + + // Get saved values + $seo_title = get_post_meta( $post->ID, '_seo_title', true ); + $seo_description = get_post_meta( $post->ID, '_seo_description', true ); + $faq_data = get_post_meta( $post->ID, '_seo_faq', true ); + if ( ! is_array( $faq_data ) ) $faq_data = array(); + ?> + + + +
+ + +
+ + +
+ + +
+ +
+ + +

+

+ +
+ + +
+

+

+ +
+ + + +
+

+

+ +
+
+

+

+ +
+
+

+

+ +
+
+

+

+ +
+ +
+ + + $questions[ $i ], + 'answer' => $answers[ $i ] + ); + } + } + update_post_meta( $post_id, '_seo_faq', $faq_pairs ); + } + + /** + * Override the tag. + */ + public function custom_title( $title ) { + if ( is_singular() ) { + $custom = get_post_meta( get_queried_object_id(), '_seo_title', true ); + if ( ! empty( $custom ) ) return $custom; + } + return $title; + } + + /** + * Output Description & Keywords. + */ + public function output_meta_tags() { + if ( ! is_singular() ) return; + $id = get_queried_object_id(); + if ( $desc = get_post_meta( $id, '_seo_description', true ) ) echo '<meta name="description" content="' . esc_attr( $desc ) . '" />' . "\n"; + // (Optional: We leave keywords empty intentionally, but if you insist, uncomment below) + // if ( $kw = get_post_meta( $id, '_seo_keywords', true ) ) echo '<meta name="keywords" content="' . esc_attr( $kw ) . '" />' . "\n"; + } + + /** + * Output Open Graph (Facebook/LinkedIn). + */ + public function output_og_tags() { + if ( ! is_singular() ) return; + $id = get_queried_object_id(); + $post = get_post( $id ); + $title = get_post_meta( $id, '_seo_title', true ) ?: $post->post_title; + $desc = get_post_meta( $id, '_seo_description', true ) ?: wp_trim_words( $post->post_content, 30 ); + echo '<meta property="og:title" content="' . esc_attr( $title ) . '" />' . "\n"; + echo '<meta property="og:description" content="' . esc_attr( $desc ) . '" />' . "\n"; + echo '<meta property="og:url" content="' . esc_url( get_permalink() ) . '" />' . "\n"; + echo '<meta property="og:type" content="article" />' . "\n"; + } + + /** + * Output JSON-LD FAQ Schema (The secret weapon for AI search). + */ + public function output_faq_schema() { + if ( ! is_singular() ) return; + $id = get_queried_object_id(); + $faq_data = get_post_meta( $id, '_seo_faq', true ); + + if ( empty( $faq_data ) || ! is_array( $faq_data ) ) return; + + $schema = array( + '@context' => 'https://schema.org', + '@type' => 'FAQPage', + 'mainEntity' => array() + ); + + foreach ( $faq_data as $faq ) { + $schema['mainEntity'][] = array( + '@type' => 'Question', + 'name' => $faq['question'], + 'acceptedAnswer' => array( + '@type' => 'Answer', + 'text' => $faq['answer'] + ) + ); + } + + echo '<script type="application/ld+json">' . json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>' . "\n"; + } +} + +new Advanced_SEO_Meta(); \ No newline at end of file