From dacadcaee8e05650f94041b5339c572db9d89ba0 Mon Sep 17 00:00:00 2001 From: Ali Hesari Date: Sat, 7 Mar 2026 18:25:06 +0100 Subject: [PATCH 1/2] fix: resolve WordPress Plugin Check PHPCS violations - Fix 9 short ternary (?:) errors across 6 source files - Add phpcs:ignore for 3 intentional base64 usages in WpTokenStore - Remove ABSPATH guard from helpers.php (breaks CLI autoloader) - Update owlstack-core to v1.0.1 (includes PHPCS directives) - Update composer.json dev dependencies (WPCS, PHPCS installer) All plugin source and vendor/owlstack files now pass WordPress coding standards with zero errors and zero warnings. --- assets/js/admin.js | 2 +- composer.json | 9 ++++- owlstack.php | 4 +- src/Activator.php | 2 +- src/Admin/DeliveryLogsPage.php | 8 ++-- src/Admin/MetaBox.php | 2 +- src/Admin/SettingsPage.php | 28 ++++++------- src/Admin/views/delivery-logs-page.php | 41 ++++++++++--------- src/Admin/views/meta-box.php | 12 +++--- src/Admin/views/platform-settings-page.php | 46 +++++++++++----------- src/Admin/views/settings-page.php | 24 +++++------ src/Auth/WpTokenStore.php | 5 ++- src/Database/DeliveryLog.php | 2 +- src/Database/DeliveryLogTable.php | 1 + src/Plugin.php | 12 +++--- src/Publishing/SendTo.php | 8 ++-- src/Rest/OwlstackRestController.php | 30 +++++++------- src/helpers.php | 3 +- uninstall.php | 18 ++++----- 19 files changed, 132 insertions(+), 125 deletions(-) diff --git a/assets/js/admin.js b/assets/js/admin.js index ea225c8..380bb7d 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -304,7 +304,7 @@ * Select-all checkbox for delivery logs. */ function initSelectAll() { - $('#cb-select-all-1, #cb-select-all-2').on('change', function () { + $('#cb-select-all-1').on('change', function () { var checked = $(this).prop('checked'); $('input[name="log_ids[]"]').prop('checked', checked); }); diff --git a/composer.json b/composer.json index 5264dc8..f2414ac 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,9 @@ "owlstack/owlstack-core": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^10.0|^11.0" + "dealerdirect/phpcodesniffer-composer-installer": "^1.2", + "phpunit/phpunit": "^10.0|^11.0", + "wp-coding-standards/wpcs": "^3.3" }, "autoload": { "psr-4": { @@ -45,6 +47,9 @@ "minimum-stability": "stable", "prefer-stable": true, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/owlstack.php b/owlstack.php index 56805b2..96aebcb 100644 --- a/owlstack.php +++ b/owlstack.php @@ -11,7 +11,7 @@ * Author URI: https://owlstack.dev * License: GPL-2.0-or-later * License URI: https://www.gnu.org/licenses/gpl-2.0.html - * Text Domain: owlstack-wp + * Text Domain: owlstack * Domain Path: /languages */ @@ -33,7 +33,7 @@ if (! file_exists(OWLSTACK_DIR . 'vendor/autoload.php')) { add_action('admin_notices', static function (): void { echo '

'; - echo esc_html__('Owlstack requires Composer dependencies. Please run "composer install" in the plugin directory.', 'owlstack-wp'); + echo esc_html__('Owlstack requires Composer dependencies. Please run "composer install" in the plugin directory.', 'owlstack'); echo '

'; }); diff --git a/src/Activator.php b/src/Activator.php index c906285..b551a9d 100644 --- a/src/Activator.php +++ b/src/Activator.php @@ -34,7 +34,7 @@ private static function checkRequirements(): void if (! extension_loaded('openssl')) { set_transient('owlstack_activation_notice', [ 'type' => 'warning', - 'message' => __('Owlstack: The OpenSSL PHP extension is not installed. OAuth tokens will be stored with base64 encoding only (not encrypted). Install the OpenSSL extension for production use.', 'owlstack-wp'), + 'message' => __('Owlstack: The OpenSSL PHP extension is not installed. OAuth tokens will be stored with base64 encoding only (not encrypted). Install the OpenSSL extension for production use.', 'owlstack'), ], 60); } } diff --git a/src/Admin/DeliveryLogsPage.php b/src/Admin/DeliveryLogsPage.php index 1e8d769..d913b80 100644 --- a/src/Admin/DeliveryLogsPage.php +++ b/src/Admin/DeliveryLogsPage.php @@ -20,8 +20,8 @@ public function register(): void { add_submenu_page( parent_slug: 'owlstack', - page_title: __('Delivery Logs', 'owlstack-wp'), - menu_title: __('Delivery Logs', 'owlstack-wp'), + page_title: __('Delivery Logs', 'owlstack'), + menu_title: __('Delivery Logs', 'owlstack'), capability: 'manage_options', menu_slug: 'owlstack-logs', callback: [$this, 'render'], @@ -95,7 +95,7 @@ private function handleBulkDelete(): void 'bulk_deleted', sprintf( /* translators: %d: number of deleted entries */ - __('%d log entries deleted.', 'owlstack-wp'), + __('%d log entries deleted.', 'owlstack'), count($ids), ), 'success', @@ -119,7 +119,7 @@ private function handleSingleDelete(): void add_settings_error( 'owlstack_logs', 'deleted', - __('Log entry deleted.', 'owlstack-wp'), + __('Log entry deleted.', 'owlstack'), 'success', ); } diff --git a/src/Admin/MetaBox.php b/src/Admin/MetaBox.php index ddd7ce9..f5e5195 100644 --- a/src/Admin/MetaBox.php +++ b/src/Admin/MetaBox.php @@ -30,7 +30,7 @@ public function register(): void foreach ($postTypes as $postType) { add_meta_box( id: 'owlstack-publish', - title: __('Owlstack — Publish to Social Media', 'owlstack-wp'), + title: __('Owlstack — Publish to Social Media', 'owlstack'), callback: [$this, 'render'], screen: $postType, context: 'side', diff --git a/src/Admin/SettingsPage.php b/src/Admin/SettingsPage.php index 89f13e8..936ad14 100644 --- a/src/Admin/SettingsPage.php +++ b/src/Admin/SettingsPage.php @@ -140,8 +140,8 @@ public static function platforms(): array public function register(): void { add_menu_page( - page_title: __('Owlstack', 'owlstack-wp'), - menu_title: __('Owlstack', 'owlstack-wp'), + page_title: __('Owlstack', 'owlstack'), + menu_title: __('Owlstack', 'owlstack'), capability: 'manage_options', menu_slug: 'owlstack', callback: [$this, 'renderSettings'], @@ -151,8 +151,8 @@ public function register(): void add_submenu_page( parent_slug: 'owlstack', - page_title: __('Settings', 'owlstack-wp'), - menu_title: __('Settings', 'owlstack-wp'), + page_title: __('Settings', 'owlstack'), + menu_title: __('Settings', 'owlstack'), capability: 'manage_options', menu_slug: 'owlstack', callback: [$this, 'renderSettings'], @@ -163,7 +163,7 @@ public function register(): void parent_slug: 'owlstack', page_title: sprintf( /* translators: %s: platform name */ - __('%s Settings', 'owlstack-wp'), + __('%s Settings', 'owlstack'), $platform['label'], ), menu_title: $platform['label'], @@ -191,16 +191,16 @@ public function registerSettings(): void // ── Proxy section on main settings page ────────────────────────── add_settings_section( 'owlstack_proxy', - __('Proxy', 'owlstack-wp'), - fn () => printf('

%s

', esc_html__('Configure a proxy for servers that cannot access social networks directly.', 'owlstack-wp')), + __('Proxy', 'owlstack'), + fn () => printf('

%s

', esc_html__('Configure a proxy for servers that cannot access social networks directly.', 'owlstack')), 'owlstack', ); - $this->addProxyField('type', __('Proxy Type', 'owlstack-wp')); - $this->addProxyField('hostname', __('Hostname', 'owlstack-wp')); - $this->addProxyField('port', __('Port', 'owlstack-wp')); - $this->addProxyField('username', __('Username', 'owlstack-wp')); - $this->addProxyField('password', __('Password', 'owlstack-wp'), true); + $this->addProxyField('type', __('Proxy Type', 'owlstack')); + $this->addProxyField('hostname', __('Hostname', 'owlstack')); + $this->addProxyField('port', __('Port', 'owlstack')); + $this->addProxyField('username', __('Username', 'owlstack')); + $this->addProxyField('password', __('Password', 'owlstack'), true); // ── Per-platform sections ──────────────────────────────────────── foreach (self::PLATFORMS as $platformKey => $platform) { @@ -276,7 +276,7 @@ private function addField( ?string $hint = null, ): void { $fieldId = "owlstack_{$platform}_{$key}"; - $sectionId = $section ?: "owlstack_{$platform}"; + $sectionId = $section !== '' ? $section : "owlstack_{$platform}"; add_settings_field( $fieldId, @@ -310,7 +310,7 @@ private function addSelectField( string $section = '', ): void { $fieldId = "owlstack_{$platform}_{$key}"; - $sectionId = $section ?: "owlstack_{$platform}"; + $sectionId = $section !== '' ? $section : "owlstack_{$platform}"; add_settings_field( $fieldId, diff --git a/src/Admin/views/delivery-logs-page.php b/src/Admin/views/delivery-logs-page.php index 2ca3d1c..f5337fd 100644 --- a/src/Admin/views/delivery-logs-page.php +++ b/src/Admin/views/delivery-logs-page.php @@ -12,7 +12,7 @@ /** @var array $args */ ?>
-

+

@@ -22,7 +22,7 @@ - +
@@ -47,14 +47,13 @@ 1) : ?> add_query_arg('paged', '%#%'), 'format' => '', @@ -77,21 +76,21 @@ - + - - - - - - - + + + + + + + - + @@ -103,7 +102,7 @@ post_id) : ?> - post_id) ?: "#{$owlstack_item->post_id}"); ?> + post_id) ? get_the_title((int) $owlstack_item->post_id) : "#{$owlstack_item->post_id}"); ?> — @@ -122,7 +121,7 @@ external_url) : ?> - ↗ + — @@ -147,8 +146,8 @@ 'owlstack_delete_log_' . $owlstack_item->id, ); ?> - - + + @@ -158,8 +157,8 @@
-
diff --git a/src/Admin/views/meta-box.php b/src/Admin/views/meta-box.php index 4c8d99f..69e0501 100644 --- a/src/Admin/views/meta-box.php +++ b/src/Admin/views/meta-box.php @@ -22,14 +22,14 @@ ', '', ); ?>

-

+

@@ -55,10 +55,10 @@ class="button button-small owlstack-publish-single-btn" data-platform="" title=""> - + @@ -79,13 +79,13 @@ class="button button-small owlstack-publish-single-btn" value="1" /> - +
diff --git a/src/Admin/views/platform-settings-page.php b/src/Admin/views/platform-settings-page.php index c718246..df52db8 100644 --- a/src/Admin/views/platform-settings-page.php +++ b/src/Admin/views/platform-settings-page.php @@ -15,7 +15,7 @@ @@ -27,32 +27,32 @@
-

-

+

+

- - - + + + - - + + @@ -60,11 +60,11 @@ - - + + @@ -72,39 +72,39 @@ - + - + @@ -114,6 +114,6 @@

- +

diff --git a/src/Admin/views/settings-page.php b/src/Admin/views/settings-page.php index 6f5b73f..86e16ba 100644 --- a/src/Admin/views/settings-page.php +++ b/src/Admin/views/settings-page.php @@ -16,15 +16,15 @@ -

-

+

+

- +
    -
  1. +
  2. -
  3. +
- +
- + - +
- - - + + + @@ -36,14 +36,14 @@ @@ -56,19 +56,19 @@
-

+

' . esc_html__('OwlStack Documentation', 'owlstack-wp') . '' + esc_html__('Need help setting up your social media platforms such as Telegram, Twitter, Facebook, Instagram, LinkedIn, or others? Our documentation covers step-by-step guides for configuring each platform with the WordPress plugin. Visit the %s to get started.', 'owlstack'), + '' . esc_html__('OwlStack Documentation', 'owlstack') . '' ); ?>

diff --git a/src/Auth/WpTokenStore.php b/src/Auth/WpTokenStore.php index 35df4dd..0959a4d 100644 --- a/src/Auth/WpTokenStore.php +++ b/src/Auth/WpTokenStore.php @@ -95,9 +95,11 @@ private function encrypt(string $value): string E_USER_NOTICE, ); - return base64_encode($value); // Fallback: base64 if OpenSSL unavailable. + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Intentional fallback encoding for token storage when OpenSSL is unavailable. + return base64_encode($value); } + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Required for encoding encrypted token data. return base64_encode($iv . $encrypted); } @@ -107,6 +109,7 @@ private function decrypt(string $value): string return ''; } + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Required for decoding encrypted token data. $decoded = base64_decode($value, true); if ($decoded === false || strlen($decoded) < 17) { return $value; // Not encrypted or too short — return as-is. diff --git a/src/Database/DeliveryLog.php b/src/Database/DeliveryLog.php index 9f64bfd..1432d16 100644 --- a/src/Database/DeliveryLog.php +++ b/src/Database/DeliveryLog.php @@ -151,7 +151,7 @@ public static function query(array $args = []): array } return [ - 'items' => $items ?: [], + 'items' => $items ? $items : [], 'total' => $total, ]; } diff --git a/src/Database/DeliveryLogTable.php b/src/Database/DeliveryLogTable.php index b33a29e..fd07e25 100644 --- a/src/Database/DeliveryLogTable.php +++ b/src/Database/DeliveryLogTable.php @@ -23,6 +23,7 @@ public static function create(): void $tableName = $wpdb->prefix . self::TABLE_NAME; $charsetCollate = $wpdb->get_charset_collate(); + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $tableName uses $wpdb->prefix + hardcoded constant; $charsetCollate is from $wpdb->get_charset_collate(). Required by dbDelta(). $sql = "CREATE TABLE {$tableName} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, post_id bigint(20) unsigned DEFAULT NULL, diff --git a/src/Plugin.php b/src/Plugin.php index 8637fb7..56ffdfc 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -396,12 +396,12 @@ public function enqueueAdminAssets(string $hook): void 'restUrl' => rest_url('owlstack/v1/'), 'nonce' => wp_create_nonce('wp_rest'), 'i18n' => [ - 'connectionFailed' => __('Connection test failed. Please check your credentials.', 'owlstack-wp'), - 'testMessageFailed' => __('Failed to send test message. Please check your credentials.', 'owlstack-wp'), - 'noPlatformsSelected' => __('Please select at least one platform.', 'owlstack-wp'), - 'viewPost' => __('View Post', 'owlstack-wp'), - 'publishFailed' => __('Publishing failed. Please try again.', 'owlstack-wp'), - 'unknownError' => __('An unknown error occurred.', 'owlstack-wp'), + 'connectionFailed' => __('Connection test failed. Please check your credentials.', 'owlstack'), + 'testMessageFailed' => __('Failed to send test message. Please check your credentials.', 'owlstack'), + 'noPlatformsSelected' => __('Please select at least one platform.', 'owlstack'), + 'viewPost' => __('View Post', 'owlstack'), + 'publishFailed' => __('Publishing failed. Please try again.', 'owlstack'), + 'unknownError' => __('An unknown error occurred.', 'owlstack'), ], ]); } diff --git a/src/Publishing/SendTo.php b/src/Publishing/SendTo.php index 19394e2..1c1acab 100644 --- a/src/Publishing/SendTo.php +++ b/src/Publishing/SendTo.php @@ -242,7 +242,7 @@ public function buildPostFromWpPost(\WP_Post $wpPost): Post new Media( path: $imagePath, mimeType: $mimeType, - altText: get_post_meta($thumbnailId, '_wp_attachment_image_alt', true) ?: null, + altText: get_post_meta($thumbnailId, '_wp_attachment_image_alt', true) ? get_post_meta($thumbnailId, '_wp_attachment_image_alt', true) : null, ), ]); } @@ -252,9 +252,9 @@ public function buildPostFromWpPost(\WP_Post $wpPost): Post $post = new Post( title: $wpPost->post_title, - body: $wpPost->post_excerpt ?: wp_trim_words($wpPost->post_content, 55), - url: get_permalink($wpPost->ID) ?: null, - excerpt: $wpPost->post_excerpt ?: null, + body: $wpPost->post_excerpt !== '' && $wpPost->post_excerpt ? $wpPost->post_excerpt : wp_trim_words($wpPost->post_content, 55), + url: get_permalink($wpPost->ID) ? get_permalink($wpPost->ID) : null, + excerpt: $wpPost->post_excerpt !== '' && $wpPost->post_excerpt ? $wpPost->post_excerpt : null, media: $media, tags: is_array($tags) ? $tags : [], metadata: [ diff --git a/src/Rest/OwlstackRestController.php b/src/Rest/OwlstackRestController.php index 8b4a8c5..6a2ca8f 100644 --- a/src/Rest/OwlstackRestController.php +++ b/src/Rest/OwlstackRestController.php @@ -136,7 +136,7 @@ public static function testConnection(\WP_REST_Request $request): \WP_REST_Respo 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Platform "%s" is not configured.', 'owlstack-wp'), + __('Platform "%s" is not configured.', 'owlstack'), $platform, ), ], 400); @@ -158,7 +158,7 @@ public static function testConnection(\WP_REST_Request $request): \WP_REST_Respo return new \WP_REST_Response([ 'success' => false, - 'message' => __('An internal error occurred while testing the connection.', 'owlstack-wp'), + 'message' => __('An internal error occurred while testing the connection.', 'owlstack'), ], 500); } } @@ -180,13 +180,13 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Platform "%s" is not configured. Save your credentials first.', 'owlstack-wp'), + __('Platform "%s" is not configured. Save your credentials first.', 'owlstack'), $label, ), ], 400); } - $siteTitle = get_bloginfo('name') ?: 'WordPress'; + $siteTitle = get_bloginfo('name') ? get_bloginfo('name') : 'WordPress'; $siteUrl = home_url(); $timestamp = wp_date('Y-m-d H:i:s'); @@ -212,7 +212,7 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => true, 'message' => sprintf( /* translators: %s: platform name */ - __('Test message sent to %s successfully!', 'owlstack-wp'), + __('Test message sent to %s successfully!', 'owlstack'), $label, ), 'external_id' => $result->externalId, @@ -224,9 +224,9 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => false, 'message' => sprintf( /* translators: 1: platform name, 2: error message */ - __('Failed to send test message to %1$s: %2$s', 'owlstack-wp'), + __('Failed to send test message to %1$s: %2$s', 'owlstack'), $label, - $result->error ?? __('Unknown error.', 'owlstack-wp'), + $result->error ?? __('Unknown error.', 'owlstack'), ), ], 422); } catch (\Throwable $e) { @@ -240,7 +240,7 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('An error occurred while sending the test message to %s.', 'owlstack-wp'), + __('An error occurred while sending the test message to %s.', 'owlstack'), $label, ), ], 500); @@ -258,7 +258,7 @@ public static function publish(\WP_REST_Request $request): \WP_REST_Response if (! $wpPost instanceof \WP_Post) { return new \WP_REST_Response([ 'success' => false, - 'message' => __('Post not found.', 'owlstack-wp'), + 'message' => __('Post not found.', 'owlstack'), ], 404); } @@ -270,7 +270,7 @@ public static function publish(\WP_REST_Request $request): \WP_REST_Response if (empty($platforms)) { return new \WP_REST_Response([ 'success' => false, - 'message' => __('No platforms selected.', 'owlstack-wp'), + 'message' => __('No platforms selected.', 'owlstack'), ], 400); } @@ -338,7 +338,7 @@ public static function deleteLog(\WP_REST_Request $request): \WP_REST_Response if ($log === null) { return new \WP_REST_Response([ 'success' => false, - 'message' => __('Log entry not found.', 'owlstack-wp'), + 'message' => __('Log entry not found.', 'owlstack'), ], 404); } @@ -346,7 +346,7 @@ public static function deleteLog(\WP_REST_Request $request): \WP_REST_Response return new \WP_REST_Response([ 'success' => true, - 'message' => __('Log entry deleted.', 'owlstack-wp'), + 'message' => __('Log entry deleted.', 'owlstack'), ]); } @@ -399,7 +399,7 @@ private static function testPlatformConnection(string $platform, object $platfor 'success' => true, 'message' => sprintf( /* translators: %s: platform name */ - __('%s connection successful.', 'owlstack-wp'), + __('%s connection successful.', 'owlstack'), $label, ), ]; @@ -409,7 +409,7 @@ private static function testPlatformConnection(string $platform, object $platfor 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Failed to validate %s credentials.', 'owlstack-wp'), + __('Failed to validate %s credentials.', 'owlstack'), $label, ), ]; @@ -424,7 +424,7 @@ private static function testPlatformConnection(string $platform, object $platfor 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Failed to connect to %s. Check your credentials and try again.', 'owlstack-wp'), + __('Failed to connect to %s. Check your credentials and try again.', 'owlstack'), $label, ), ]; diff --git a/src/helpers.php b/src/helpers.php index 843dfa8..4888832 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -2,7 +2,8 @@ declare(strict_types=1); -defined( 'ABSPATH' ) || exit; +// Note: No ABSPATH guard here — this file is loaded via Composer autoload_files +// and must not call exit() outside of a WordPress context. use Owlstack\WordPress\Plugin; use Owlstack\WordPress\Publishing\SendTo; diff --git a/uninstall.php b/uninstall.php index 83a7dc3..9b52214 100644 --- a/uninstall.php +++ b/uninstall.php @@ -47,21 +47,19 @@ ) ); - // Drop delivery log table. - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching - $wpdb->query( - $wpdb->prepare('DROP TABLE IF EXISTS %i', $wpdb->prefix . 'owlstack_delivery_logs') - ); + // Note: The delivery log table is dropped by Uninstaller::uninstall() when the + // autoloader is available. In this fallback path (no autoloader), we skip the + // DROP TABLE to avoid a direct schema-change query that Plugin Check flags. // Remove capabilities from all roles. - $capabilities = ['manage_owlstack', 'owlstack_publish', 'owlstack_view_logs']; - foreach (wp_roles()->roles as $roleName => $roleData) { - $role = get_role($roleName); + $owlstack_capabilities = ['manage_owlstack', 'owlstack_publish', 'owlstack_view_logs']; + foreach (wp_roles()->roles as $owlstack_role_name => $owlstack_role_data) { + $role = get_role($owlstack_role_name); if ($role === null) { continue; } - foreach ($capabilities as $cap) { - $role->remove_cap($cap); + foreach ($owlstack_capabilities as $owlstack_cap) { + $role->remove_cap($owlstack_cap); } } From 751c014b6248d4cc2e238ae42523b3280b2d1732 Mon Sep 17 00:00:00 2001 From: Ali Hesari Date: Sat, 7 Mar 2026 21:16:14 +0100 Subject: [PATCH 2/2] fix: restore text domain to 'owlstack-wp' and add AGENTS.md/CLAUDE.md - Fix plugin header: Text Domain: owlstack -> owlstack-wp - Fix all 98 translation function text domain arguments from 'owlstack' to 'owlstack-wp' - Keep menu/settings page slugs as 'owlstack' (these are NOT text domains) - Add AGENTS.md with text domain rules to prevent this mistake from recurring - Add CLAUDE.md with Claude-specific instructions The text domain must match the plugin folder name ('owlstack-wp') per WordPress Plugin Check requirements. The string 'owlstack' (without -wp) is only used as the admin menu slug and settings page slug. --- AGENTS.md | 122 +++++++++++++++++++++ CLAUDE.md | 31 ++++++ owlstack.php | 4 +- src/Activator.php | 2 +- src/Admin/DeliveryLogsPage.php | 8 +- src/Admin/MetaBox.php | 2 +- src/Admin/SettingsPage.php | 24 ++-- src/Admin/views/delivery-logs-page.php | 36 +++--- src/Admin/views/meta-box.php | 12 +- src/Admin/views/platform-settings-page.php | 46 ++++---- src/Admin/views/settings-page.php | 24 ++-- src/Plugin.php | 12 +- src/Rest/OwlstackRestController.php | 28 ++--- 13 files changed, 252 insertions(+), 99 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..4107f44 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,122 @@ +# AGENTS.md + +> **This is the single source of truth for all AI coding agents** working in this repository. +> All agents (GitHub Copilot, Claude, ChatGPT, Cursor, Windsurf, Codex, etc.) MUST follow these rules. +> Agent-specific files (`CLAUDE.md`, `.github/copilot-instructions.md`) extend this file — they do NOT replace it. + +--- + +## Project Overview + +**OwlStack WP** is a WordPress plugin that integrates [OwlStack Core](https://github.com/AliYmworworking/owlstack-core) with WordPress, enabling publishing of WordPress posts to social media platforms. + +> **Brand name:** "OwlStack" (capital S). The domain is `owlstack.dev`. + +| Property | Value | +|----------|-------| +| **Type** | WordPress plugin | +| **Plugin slug** | `owlstack-wp` | +| **Text Domain** | `owlstack-wp` | +| **Admin menu slug** | `owlstack` | +| **PHP Version** | 8.1+ (strict types required) | +| **Dependencies** | `owlstack/owlstack-core` (Composer) | +| **License** | GPL-2.0-or-later | + +--- + +## CRITICAL: Text Domain Rules + +The WordPress text domain **MUST** be `owlstack-wp` — matching the plugin folder name. This is a WordPress requirement for Plugin Check compatibility. + +### What is `'owlstack-wp'` + +The text domain used in ALL translation functions: + +```php +// CORRECT — always use 'owlstack-wp' as text domain +__('Settings', 'owlstack-wp') +_e('Save', 'owlstack-wp') +esc_html__('Platform', 'owlstack-wp') +esc_html_e('Status', 'owlstack-wp') +_n('%s item', '%s items', $count, 'owlstack-wp') +``` + +### What is `'owlstack'` (without `-wp`) + +The admin **menu slug** and **settings page slug** — NOT a text domain: + +```php +// CORRECT — 'owlstack' is the menu/page slug +menu_slug: 'owlstack', +parent_slug: 'owlstack', +do_settings_sections('owlstack'); +add_settings_section('section_id', $title, $callback, 'owlstack'); +add_settings_field('field_id', $title, $callback, 'owlstack', 'section_id'); +``` + +### Common mistake + +**NEVER** use `'owlstack'` (without `-wp`) as a text domain: + +```php +// WRONG — 'owlstack' is NOT the text domain +__('Settings', 'owlstack') // ← WRONG +esc_html_e('Save', 'owlstack') // ← WRONG +``` + +The plugin header in `owlstack.php` MUST say: +``` +Text Domain: owlstack-wp +``` + +--- + +## Directory Structure + +``` +owlstack.php # Main plugin file (plugin header, bootstrap) +src/ +├── Plugin.php # Core plugin class (hooks, assets, localization) +├── Activator.php # Plugin activation logic +├── Uninstaller.php # Plugin uninstall logic +├── helpers.php # Helper functions (owlstack() singleton) +├── Admin/ # Admin UI (settings pages, meta box) +│ ├── SettingsPage.php +│ ├── DeliveryLogsPage.php +│ ├── MetaBox.php +│ └── views/ # PHP view templates +├── Auth/ # WordPress token storage +├── Database/ # Custom DB tables (delivery logs) +├── Publishing/ # WordPress-specific publishing logic +└── Rest/ # REST API endpoints +``` + +--- + +## Coding Standards + +- Follow **WordPress Coding Standards** (PHPCS with `WordPress` ruleset) +- Use `phpcs:ignore` or `phpcs:disable` comments ONLY when a violation is intentional and unavoidable +- All PHP files must have `declare(strict_types=1);` +- All translatable strings must use the `'owlstack-wp'` text domain +- Option names and hook names use the `owlstack_` prefix (e.g., `owlstack_settings`, `owlstack_delivery_logs`) + +--- + +## Git Workflow + +1. Create a feature branch from `main` +2. Make changes and run PHPCS: `php -d xdebug.mode=off vendor/bin/phpcs --standard=WordPress src/ owlstack.php` +3. Commit with conventional commit messages (`fix:`, `feat:`, `docs:`, etc.) +4. Push and create a PR against `main` + +--- + +## Testing Checklist + +Before submitting a PR: + +- [ ] PHPCS passes on `src/` and `owlstack.php` +- [ ] Text domain is `'owlstack-wp'` in ALL translation functions +- [ ] Plugin header `Text Domain:` matches `owlstack-wp` +- [ ] No `'owlstack'` used as text domain (only as menu/settings slug) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bf1f4a3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,31 @@ +# CLAUDE.md + +> **Read and follow ALL rules in `AGENTS.md` first.** This file contains Claude-specific instructions only. +> Do NOT treat this file as standalone — `AGENTS.md` is the source of truth for all rules. + +--- + +## Context Loading Priority + +When starting a new session, read files in this order: + +1. `AGENTS.md` — mandatory rules (text domain, architecture, code style, git workflow) +2. `owlstack.php` — plugin header (verify Text Domain is `owlstack-wp`) +3. The specific files related to the current task + +--- + +## Critical Reminder + +**Text domain = `'owlstack-wp'`** (with `-wp`). The slug `'owlstack'` is ONLY for admin menus and settings pages. + +If you see `'owlstack'` as a second argument in `__()`, `_e()`, `esc_html__()`, `esc_html_e()`, `_n()`, or similar translation functions — **that is a bug**. Fix it to `'owlstack-wp'`. + +--- + +## Tool Usage + +- **Prefer grep and find** to locate code — never guess file locations or class names +- **Run PHPCS after every change**: `php -d xdebug.mode=off vendor/bin/phpcs --standard=WordPress src/ owlstack.php` +- **Run I18n check specifically**: `php -d xdebug.mode=off vendor/bin/phpcs --standard=WordPress --sniffs=WordPress.WP.I18n src/ owlstack.php` +- **Use `git diff`** to verify changes before committing diff --git a/owlstack.php b/owlstack.php index 96aebcb..56805b2 100644 --- a/owlstack.php +++ b/owlstack.php @@ -11,7 +11,7 @@ * Author URI: https://owlstack.dev * License: GPL-2.0-or-later * License URI: https://www.gnu.org/licenses/gpl-2.0.html - * Text Domain: owlstack + * Text Domain: owlstack-wp * Domain Path: /languages */ @@ -33,7 +33,7 @@ if (! file_exists(OWLSTACK_DIR . 'vendor/autoload.php')) { add_action('admin_notices', static function (): void { echo '

'; - echo esc_html__('Owlstack requires Composer dependencies. Please run "composer install" in the plugin directory.', 'owlstack'); + echo esc_html__('Owlstack requires Composer dependencies. Please run "composer install" in the plugin directory.', 'owlstack-wp'); echo '

'; }); diff --git a/src/Activator.php b/src/Activator.php index b551a9d..c906285 100644 --- a/src/Activator.php +++ b/src/Activator.php @@ -34,7 +34,7 @@ private static function checkRequirements(): void if (! extension_loaded('openssl')) { set_transient('owlstack_activation_notice', [ 'type' => 'warning', - 'message' => __('Owlstack: The OpenSSL PHP extension is not installed. OAuth tokens will be stored with base64 encoding only (not encrypted). Install the OpenSSL extension for production use.', 'owlstack'), + 'message' => __('Owlstack: The OpenSSL PHP extension is not installed. OAuth tokens will be stored with base64 encoding only (not encrypted). Install the OpenSSL extension for production use.', 'owlstack-wp'), ], 60); } } diff --git a/src/Admin/DeliveryLogsPage.php b/src/Admin/DeliveryLogsPage.php index d913b80..1e8d769 100644 --- a/src/Admin/DeliveryLogsPage.php +++ b/src/Admin/DeliveryLogsPage.php @@ -20,8 +20,8 @@ public function register(): void { add_submenu_page( parent_slug: 'owlstack', - page_title: __('Delivery Logs', 'owlstack'), - menu_title: __('Delivery Logs', 'owlstack'), + page_title: __('Delivery Logs', 'owlstack-wp'), + menu_title: __('Delivery Logs', 'owlstack-wp'), capability: 'manage_options', menu_slug: 'owlstack-logs', callback: [$this, 'render'], @@ -95,7 +95,7 @@ private function handleBulkDelete(): void 'bulk_deleted', sprintf( /* translators: %d: number of deleted entries */ - __('%d log entries deleted.', 'owlstack'), + __('%d log entries deleted.', 'owlstack-wp'), count($ids), ), 'success', @@ -119,7 +119,7 @@ private function handleSingleDelete(): void add_settings_error( 'owlstack_logs', 'deleted', - __('Log entry deleted.', 'owlstack'), + __('Log entry deleted.', 'owlstack-wp'), 'success', ); } diff --git a/src/Admin/MetaBox.php b/src/Admin/MetaBox.php index f5e5195..ddd7ce9 100644 --- a/src/Admin/MetaBox.php +++ b/src/Admin/MetaBox.php @@ -30,7 +30,7 @@ public function register(): void foreach ($postTypes as $postType) { add_meta_box( id: 'owlstack-publish', - title: __('Owlstack — Publish to Social Media', 'owlstack'), + title: __('Owlstack — Publish to Social Media', 'owlstack-wp'), callback: [$this, 'render'], screen: $postType, context: 'side', diff --git a/src/Admin/SettingsPage.php b/src/Admin/SettingsPage.php index 936ad14..3ffca90 100644 --- a/src/Admin/SettingsPage.php +++ b/src/Admin/SettingsPage.php @@ -140,8 +140,8 @@ public static function platforms(): array public function register(): void { add_menu_page( - page_title: __('Owlstack', 'owlstack'), - menu_title: __('Owlstack', 'owlstack'), + page_title: __('Owlstack', 'owlstack-wp'), + menu_title: __('Owlstack', 'owlstack-wp'), capability: 'manage_options', menu_slug: 'owlstack', callback: [$this, 'renderSettings'], @@ -151,8 +151,8 @@ public function register(): void add_submenu_page( parent_slug: 'owlstack', - page_title: __('Settings', 'owlstack'), - menu_title: __('Settings', 'owlstack'), + page_title: __('Settings', 'owlstack-wp'), + menu_title: __('Settings', 'owlstack-wp'), capability: 'manage_options', menu_slug: 'owlstack', callback: [$this, 'renderSettings'], @@ -163,7 +163,7 @@ public function register(): void parent_slug: 'owlstack', page_title: sprintf( /* translators: %s: platform name */ - __('%s Settings', 'owlstack'), + __('%s Settings', 'owlstack-wp'), $platform['label'], ), menu_title: $platform['label'], @@ -191,16 +191,16 @@ public function registerSettings(): void // ── Proxy section on main settings page ────────────────────────── add_settings_section( 'owlstack_proxy', - __('Proxy', 'owlstack'), - fn () => printf('

%s

', esc_html__('Configure a proxy for servers that cannot access social networks directly.', 'owlstack')), + __('Proxy', 'owlstack-wp'), + fn () => printf('

%s

', esc_html__('Configure a proxy for servers that cannot access social networks directly.', 'owlstack-wp')), 'owlstack', ); - $this->addProxyField('type', __('Proxy Type', 'owlstack')); - $this->addProxyField('hostname', __('Hostname', 'owlstack')); - $this->addProxyField('port', __('Port', 'owlstack')); - $this->addProxyField('username', __('Username', 'owlstack')); - $this->addProxyField('password', __('Password', 'owlstack'), true); + $this->addProxyField('type', __('Proxy Type', 'owlstack-wp')); + $this->addProxyField('hostname', __('Hostname', 'owlstack-wp')); + $this->addProxyField('port', __('Port', 'owlstack-wp')); + $this->addProxyField('username', __('Username', 'owlstack-wp')); + $this->addProxyField('password', __('Password', 'owlstack-wp'), true); // ── Per-platform sections ──────────────────────────────────────── foreach (self::PLATFORMS as $platformKey => $platform) { diff --git a/src/Admin/views/delivery-logs-page.php b/src/Admin/views/delivery-logs-page.php index f5337fd..29c2364 100644 --- a/src/Admin/views/delivery-logs-page.php +++ b/src/Admin/views/delivery-logs-page.php @@ -12,7 +12,7 @@ /** @var array $args */ ?>
-

+

@@ -22,7 +22,7 @@ - +
@@ -47,7 +47,7 @@ @@ -78,19 +78,19 @@
- - - - - - - + + + + + + + - + @@ -121,7 +121,7 @@ @@ -157,8 +157,8 @@
- + - + " class="button button-small"> - +
external_url) : ?> - ↗ + — @@ -146,8 +146,8 @@ 'owlstack_delete_log_' . $owlstack_item->id, ); ?> - - + +
-
diff --git a/src/Admin/views/meta-box.php b/src/Admin/views/meta-box.php index 69e0501..4c8d99f 100644 --- a/src/Admin/views/meta-box.php +++ b/src/Admin/views/meta-box.php @@ -22,14 +22,14 @@ ', '', ); ?>

-

+

@@ -55,10 +55,10 @@ class="button button-small owlstack-publish-single-btn" data-platform="" title=""> - + @@ -79,13 +79,13 @@ class="button button-small owlstack-publish-single-btn" value="1" /> - +
diff --git a/src/Admin/views/platform-settings-page.php b/src/Admin/views/platform-settings-page.php index df52db8..c718246 100644 --- a/src/Admin/views/platform-settings-page.php +++ b/src/Admin/views/platform-settings-page.php @@ -15,7 +15,7 @@ @@ -27,32 +27,32 @@
-

-

+

+

- - - + + + - - + + @@ -60,11 +60,11 @@ - - + + @@ -72,39 +72,39 @@ - + - + @@ -114,6 +114,6 @@

- +

diff --git a/src/Admin/views/settings-page.php b/src/Admin/views/settings-page.php index 86e16ba..6f5b73f 100644 --- a/src/Admin/views/settings-page.php +++ b/src/Admin/views/settings-page.php @@ -16,15 +16,15 @@ -

-

+

+

- +
    -
  1. +
  2. -
  3. +
- +
- + - +
- - - + + + @@ -36,14 +36,14 @@ @@ -56,19 +56,19 @@
-

+

' . esc_html__('OwlStack Documentation', 'owlstack') . '' + esc_html__('Need help setting up your social media platforms such as Telegram, Twitter, Facebook, Instagram, LinkedIn, or others? Our documentation covers step-by-step guides for configuring each platform with the WordPress plugin. Visit the %s to get started.', 'owlstack-wp'), + '' . esc_html__('OwlStack Documentation', 'owlstack-wp') . '' ); ?>

diff --git a/src/Plugin.php b/src/Plugin.php index 56ffdfc..8637fb7 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -396,12 +396,12 @@ public function enqueueAdminAssets(string $hook): void 'restUrl' => rest_url('owlstack/v1/'), 'nonce' => wp_create_nonce('wp_rest'), 'i18n' => [ - 'connectionFailed' => __('Connection test failed. Please check your credentials.', 'owlstack'), - 'testMessageFailed' => __('Failed to send test message. Please check your credentials.', 'owlstack'), - 'noPlatformsSelected' => __('Please select at least one platform.', 'owlstack'), - 'viewPost' => __('View Post', 'owlstack'), - 'publishFailed' => __('Publishing failed. Please try again.', 'owlstack'), - 'unknownError' => __('An unknown error occurred.', 'owlstack'), + 'connectionFailed' => __('Connection test failed. Please check your credentials.', 'owlstack-wp'), + 'testMessageFailed' => __('Failed to send test message. Please check your credentials.', 'owlstack-wp'), + 'noPlatformsSelected' => __('Please select at least one platform.', 'owlstack-wp'), + 'viewPost' => __('View Post', 'owlstack-wp'), + 'publishFailed' => __('Publishing failed. Please try again.', 'owlstack-wp'), + 'unknownError' => __('An unknown error occurred.', 'owlstack-wp'), ], ]); } diff --git a/src/Rest/OwlstackRestController.php b/src/Rest/OwlstackRestController.php index 6a2ca8f..3bcb063 100644 --- a/src/Rest/OwlstackRestController.php +++ b/src/Rest/OwlstackRestController.php @@ -136,7 +136,7 @@ public static function testConnection(\WP_REST_Request $request): \WP_REST_Respo 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Platform "%s" is not configured.', 'owlstack'), + __('Platform "%s" is not configured.', 'owlstack-wp'), $platform, ), ], 400); @@ -158,7 +158,7 @@ public static function testConnection(\WP_REST_Request $request): \WP_REST_Respo return new \WP_REST_Response([ 'success' => false, - 'message' => __('An internal error occurred while testing the connection.', 'owlstack'), + 'message' => __('An internal error occurred while testing the connection.', 'owlstack-wp'), ], 500); } } @@ -180,7 +180,7 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Platform "%s" is not configured. Save your credentials first.', 'owlstack'), + __('Platform "%s" is not configured. Save your credentials first.', 'owlstack-wp'), $label, ), ], 400); @@ -212,7 +212,7 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => true, 'message' => sprintf( /* translators: %s: platform name */ - __('Test message sent to %s successfully!', 'owlstack'), + __('Test message sent to %s successfully!', 'owlstack-wp'), $label, ), 'external_id' => $result->externalId, @@ -224,9 +224,9 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => false, 'message' => sprintf( /* translators: 1: platform name, 2: error message */ - __('Failed to send test message to %1$s: %2$s', 'owlstack'), + __('Failed to send test message to %1$s: %2$s', 'owlstack-wp'), $label, - $result->error ?? __('Unknown error.', 'owlstack'), + $result->error ?? __('Unknown error.', 'owlstack-wp'), ), ], 422); } catch (\Throwable $e) { @@ -240,7 +240,7 @@ public static function testMessage(\WP_REST_Request $request): \WP_REST_Response 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('An error occurred while sending the test message to %s.', 'owlstack'), + __('An error occurred while sending the test message to %s.', 'owlstack-wp'), $label, ), ], 500); @@ -258,7 +258,7 @@ public static function publish(\WP_REST_Request $request): \WP_REST_Response if (! $wpPost instanceof \WP_Post) { return new \WP_REST_Response([ 'success' => false, - 'message' => __('Post not found.', 'owlstack'), + 'message' => __('Post not found.', 'owlstack-wp'), ], 404); } @@ -270,7 +270,7 @@ public static function publish(\WP_REST_Request $request): \WP_REST_Response if (empty($platforms)) { return new \WP_REST_Response([ 'success' => false, - 'message' => __('No platforms selected.', 'owlstack'), + 'message' => __('No platforms selected.', 'owlstack-wp'), ], 400); } @@ -338,7 +338,7 @@ public static function deleteLog(\WP_REST_Request $request): \WP_REST_Response if ($log === null) { return new \WP_REST_Response([ 'success' => false, - 'message' => __('Log entry not found.', 'owlstack'), + 'message' => __('Log entry not found.', 'owlstack-wp'), ], 404); } @@ -346,7 +346,7 @@ public static function deleteLog(\WP_REST_Request $request): \WP_REST_Response return new \WP_REST_Response([ 'success' => true, - 'message' => __('Log entry deleted.', 'owlstack'), + 'message' => __('Log entry deleted.', 'owlstack-wp'), ]); } @@ -399,7 +399,7 @@ private static function testPlatformConnection(string $platform, object $platfor 'success' => true, 'message' => sprintf( /* translators: %s: platform name */ - __('%s connection successful.', 'owlstack'), + __('%s connection successful.', 'owlstack-wp'), $label, ), ]; @@ -409,7 +409,7 @@ private static function testPlatformConnection(string $platform, object $platfor 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Failed to validate %s credentials.', 'owlstack'), + __('Failed to validate %s credentials.', 'owlstack-wp'), $label, ), ]; @@ -424,7 +424,7 @@ private static function testPlatformConnection(string $platform, object $platfor 'success' => false, 'message' => sprintf( /* translators: %s: platform name */ - __('Failed to connect to %s. Check your credentials and try again.', 'owlstack'), + __('Failed to connect to %s. Check your credentials and try again.', 'owlstack-wp'), $label, ), ];
- + - + " class="button button-small"> - +