Skip to content

Commit dec4b62

Browse files
committed
Update AGENTS.md and settings sanitize
1 parent b273210 commit dec4b62

2 files changed

Lines changed: 27 additions & 27 deletions

File tree

AGENTS.md

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file provides guidance to Codex (Codex.ai/code) when working with code in t
44

55
## Plugin Overview
66

7-
WebberZone Knowledge Base Pro (v3.1.0 is currently in development) is a WordPress plugin (namespace `WebberZone\Knowledge_Base`) that creates a multi-product knowledge base system. It uses a freemium model via Freemius integration free core features with premium features in `/includes/pro/`.
7+
WebberZone Knowledge Base Pro (v3.1.0 is currently in development) is a WordPress plugin (namespace `WebberZone\Knowledge_Base`) that creates a multi-product knowledge base system. It uses a freemium model via Freemius integration -- free core features with premium features in `/includes/pro/`.
88

99
- **Plugin entry**: `knowledgebase.php` (defines constants, loads Freemius via `load-freemius.php`, registers autoloader, and directly requires `includes/options-api.php` and `includes/functions.php`)
1010
- **PHP**: 7.4+ | **WordPress**: 6.7+
@@ -20,7 +20,7 @@ composer test # Run phpcs + phpcompat + phpstan
2020
composer phpcs # WordPress coding standards check
2121
composer phpcbf # Auto-fix coding standards
2222
composer phpstan # Static analysis (Level 5)
23-
composer phpcompat # PHP 7.48.5 compatibility check
23+
composer phpcompat # PHP 7.4-8.5 compatibility check
2424
vendor/bin/phpunit # Run unit tests
2525
vendor/bin/phpunit --filter TestName # Run a single test by name
2626
WP_MULTISITE=1 vendor/bin/phpunit # Run multisite unit tests
@@ -64,21 +64,21 @@ npm run zip # Create full plugin zip
6464

6565
### Main Bootstrap Flow
6666

67-
1. `plugins_loaded` hook `Main::get_instance()` (singleton)
67+
1. `plugins_loaded` hook -> `Main::get_instance()` (singleton)
6868
2. `Main::init()` instantiates all component handlers and registers their hooks
6969
3. Admin components only load on `is_admin()`; Pro components only if Freemius detects a premium license
7070

7171
### Key Patterns
7272

73-
**Autoloader** (`includes/autoloader.php`): PSR-4 style. Converts `WebberZone\Knowledge_Base\Admin\Settings` `includes/admin/class-settings.php`.
73+
**Autoloader** (`includes/autoloader.php`): PSR-4 style. Converts `WebberZone\Knowledge_Base\Admin\Settings` -> `includes/admin/class-settings.php`.
7474

7575
**Hook Registry** (`includes/util/class-hook-registry.php`): Custom wrapper around WordPress actions/filters with duplicate prevention and closure support. All components register hooks through this instead of calling `add_action()`/`add_filter()` directly.
7676

7777
**Settings**: Global `$wzkb_settings` populated at plugin load. Read via `wzkb_get_option( $key )` or `wzkb_get_settings()`. Settings page in `includes/admin/class-settings.php`.
7878

7979
**Caching** (`includes/util/class-cache.php`): Term meta-based caching (not transients) with expiry timestamps. AJAX endpoint for admin cache clearing. Use atomic operations when modifying cached data.
8080

81-
**Free vs Pro**: The pro plugin (`knowledgebase-pro/`) is a **standalone, complete replacement** for the free plugin (`knowledgebase/`). It contains its own full copy of all free files (e.g. `includes/frontend/class-shortcodes.php`) **plus** the premium-only code in `includes/pro/`. Activating either version auto-deactivates the other. When adding or editing free-tier features, always edit the file inside `knowledgebase-pro/` never the sibling `knowledgebase/` directory. Pro-only features are conditionally instantiated in `Main::init()` and live exclusively in `includes/pro/`.
81+
**Free vs Pro**: The pro plugin (`knowledgebase-pro/`) is a **standalone, complete replacement** for the free plugin (`knowledgebase/`). It contains its own full copy of all free files (e.g. `includes/frontend/class-shortcodes.php`) **plus** the premium-only code in `includes/pro/`. Activating either version auto-deactivates the other. When adding or editing free-tier features, always edit the file inside `knowledgebase-pro/` -- never the sibling `knowledgebase/` directory. Pro-only features are conditionally instantiated in `Main::init()` and live exclusively in `includes/pro/`.
8282

8383
### Component Map
8484

@@ -88,7 +88,7 @@ npm run zip # Create full plugin zip
8888
| `includes/frontend/` | Templates, display, shortcodes, styles, search, breadcrumbs, related articles, feeds |
8989
| `includes/blocks/` | 8 free Gutenberg blocks (React in `src/`, compiled to `build/`) |
9090
| `includes/pro/` | Premium features: custom permalinks, rating system, help widget, KB homepage mode, GitHub import |
91-
| `includes/pro/github/` | GitHub Markdown import: API wrapper, content converter, import processor, webhook handler, import wizard, link rewriter |
91+
| `includes/pro/github/` | GitHub Markdown import: API wrapper, content importer, import processor, webhook handler, import wizard, link rewriter |
9292
| `includes/rest/` | REST API under `/wzkb/v1/` namespace |
9393
| `includes/widgets/` | 4 classic WordPress widgets |
9494
| `includes/util/` | Hook registry, caching utilities |
@@ -110,38 +110,38 @@ All shortcodes live in `includes/frontend/class-shortcodes.php` (free-tier, pres
110110

111111
Imports Markdown docs from GitHub repositories into KB articles. All classes are in `includes/pro/github/`.
112112

113-
- **`class-api.php`** GitHub REST API wrapper (PAT auth, Git Trees, Contents, token validation). Filter: `wzkb_github_api_args`.
114-
- **`class-content-importer.php`** Converts Markdown Gutenberg blocks (or classic HTML). Handles frontmatter parsing, `[toc]` `knowledgebase/toc` block or `[kbtoc]` shortcode, image URL resolution. Image blocks always hand-build their `<figure>/<img/>` HTML never use `outer_html()` (DOMDocument) for image output, as it introduces whitespace and non-self-closing tags that fail Gutenberg block validation.
115-
- **`class-import-processor.php`** Core importer: SHA change detection, taxonomy assignment (`sections``wzkb_category`, `tags``wzkb_tag`, `products``wzkb_product`), image sideloading, rename/delete handlers. All three taxonomy types auto-create missing terms by slug. `_wzkb_github_source_url` is populated from a constructed `github.com` blob URL (Git Trees API does not return `html_url`). Developer hooks: `wzkb_github_skip_file`, `wzkb_github_pre_import`, `wzkb_github_post_import`, `wzkb_github_markdown_html`. `fix_image_block_attrs()` runs after sideloading to rebuild `<!-- wp:image -->` comments only non-sourced attrs (`id`, `sizeSlug`, `linkDestination`) go in the comment; `url`/`alt` are sourced and must be omitted or Gutenberg triggers "Attempt to recover".
116-
- **`class-link-rewriter.php`** Rewrites relative `.md` hrefs to WP post permalinks using a path-map transient (`wzkb_github_path_map`, 24 hr TTL).
117-
- **`class-webhook-handler.php`** REST endpoint `POST /wzkb/v1/github/webhook` (HMAC-SHA256 validated). Handles push events: added/modified/removed/renamed files. Passes `mapping['branch']` as `$ref` to `process_file()` do not hardcode `''` here. Accepts both `.md` and `.markdown` extensions. Admin validate endpoint: `GET /wzkb/v1/github/validate`.
118-
- **`class-import-wizard.php`** Admin UI page (`wzkb-github-import`) for manual one-off imports. AJAX-driven: `wzkb_github_import_list_files` builds the task list (with SHA pre-skip detection), `wzkb_github_import_process_one` processes a single file and returns result data including permalink and taxonomy terms. Script: `includes/admin/js/github-import-wizard.js`, localised as `WZKBImportWizard`.
113+
- **`class-api.php`** -- GitHub REST API wrapper (PAT auth, Git Trees, Contents, token validation). Filter: `wzkb_github_api_args`.
114+
- **`class-content-converter.php`** -- Converts Markdown -> Gutenberg blocks (or classic HTML). Handles frontmatter parsing, `[toc]` -> `knowledgebase/toc` block or `[kbtoc]` shortcode, image URL resolution. Image blocks always hand-build their `<figure>/<img/>` HTML -- never use `outer_html()` (DOMDocument) for image output, as it introduces whitespace and non-self-closing tags that fail Gutenberg block validation.
115+
- **`class-import-processor.php`** -- Core importer: SHA change detection, taxonomy assignment (`sections`->`wzkb_category`, `tags`->`wzkb_tag`, `products`->`wzkb_product`), image sideloading, rename/delete handlers. All three taxonomy types auto-create missing terms by slug. `_wzkb_github_source_url` is populated from a constructed `github.com` blob URL (Git Trees API does not return `html_url`). Developer hooks: `wzkb_github_skip_file`, `wzkb_github_pre_import`, `wzkb_github_post_import`, `wzkb_github_markdown_html`. `fix_image_block_attrs()` runs after sideloading to rebuild `<!-- wp:image -->` comments -- only non-sourced attrs (`id`, `sizeSlug`, `linkDestination`) go in the comment; `url`/`alt` are sourced and must be omitted or Gutenberg triggers "Attempt to recover".
116+
- **`class-link-rewriter.php`** -- Rewrites relative `.md` hrefs to WP post permalinks using a path-map transient (`wzkb_github_path_map`, 24 hr TTL).
117+
- **`class-webhook-handler.php`** -- REST endpoint `POST /wzkb/v1/github/webhook` (HMAC-SHA256 validated). Handles push events: added/modified/removed/renamed files. Passes `mapping['branch']` as `$ref` to `process_file()` -- do not hardcode `''` here. Accepts both `.md` and `.markdown` extensions. Admin validate endpoint: `GET /wzkb/v1/github/validate`.
118+
- **`class-import-wizard.php`** -- Admin UI page (`wzkb-github-import`) for manual one-off imports. AJAX-driven: `wzkb_github_import_list_files` builds the task list (with SHA pre-skip detection), `wzkb_github_import_process_one` processes a single file and returns result data including permalink and taxonomy terms. Script: `includes/admin/js/github-import-wizard.js`, localised as `WZKBImportWizard`.
119119

120-
**`Import_Processor` public surface**: `get_file_list( $owner, $repo, $mapping, $ref )` wraps `list_markdown_files`; `get_pre_skip_info( $owner, $repo, $path, $tree_sha )` returns existing post data if SHA unchanged, `null` otherwise. `find_github_post()` is `protected` (not private) subclasses can override.
120+
**`Import_Processor` public surface**: `get_file_list( $owner, $repo, $mapping, $ref )` wraps `list_markdown_files`; `get_pre_skip_info( $owner, $repo, $path, $tree_sha )` returns existing post data if SHA unchanged, `null` otherwise. `find_github_post()` is `protected` (not private) -- subclasses can override.
121121

122-
**Repeater `live_update_field_options`**: pass an `id label` map in the repeater field args as `'live_update_field_options' => [ id => name ]`; `class-settings-form.php` emits it as `data-live-update-field-options` JSON on the wrapper div; JS reads it to resolve raw values (e.g. term IDs) to human-readable titles in the repeater row header.
122+
**Repeater `live_update_field_options`**: pass an `id -> label` map in the repeater field args as `'live_update_field_options' => [ id => name ]`; `class-settings-form.php` emits it as `data-live-update-field-options` JSON on the wrapper div; JS reads it to resolve raw values (e.g. term IDs) to human-readable titles in the repeater row header.
123123

124124
**Post meta keys** stored per imported article: `_wzkb_github_repo`, `_wzkb_github_path`, `_wzkb_github_sha`, `_wzkb_github_last_sync`, `_wzkb_github_source_url`, `_wzkb_github_doc_id`.
125125

126-
**Frontmatter fields** (YAML at top of `.md` file): `title`, `sections`/`categories`/`category`/`section` ( `wzkb_category`), `tags`/`tag` ( `wzkb_tag`), `products`/`product` ( `wzkb_product`), `order`/`menu_order`, `status`, `toc` (bool). `sections` supports path notation for hierarchy: `"Parent/Child"` finds or creates `Child` as a term under `Parent`; plain slugs without `/` remain top-level.
126+
**Frontmatter fields** (YAML at top of `.md` file): `title`, `sections`/`categories`/`category`/`section` (-> `wzkb_category`), `tags`/`tag` (-> `wzkb_tag`), `products`/`product` (-> `wzkb_product`), `order`/`menu_order`, `status`, `toc` (bool). `sections` supports path notation for hierarchy: `"Parent/Child"` finds or creates `Child` as a term under `Parent`; plain slugs without `/` remain top-level.
127127

128-
**Repository mappings** are configured in Settings GitHub tab as a repeater (`github_repositories`). Each mapping has: `repo_owner`, `repo_name`, `folder_path`, `product_id`, `branch`, `pat`, `default_status`, `duplicate_handling`, `delete_removed`, `status`. The per-mapping `pat` field (sensitive, encrypted) overrides the global `github_pat` for that mapping use this when repositories belong to different owners or organisations (fine-grained PATs are scoped per owner). The global `github_pat` and `github_webhook_secret` are also `sensitive` type (encrypted at rest). `API::with_pat( $pat )` returns a cloned API instance with the override applied; `Import_Processor::api_for_mapping( $mapping )` selects the right instance automatically.
128+
**Repository mappings** are configured in Settings -> GitHub tab as a repeater (`github_repositories`). Each mapping has: `repo_owner`, `repo_name`, `folder_path`, `product_id`, `branch`, `pat`, `default_status`, `duplicate_handling`, `delete_removed`, `status`. The per-mapping `pat` field (sensitive, encrypted) overrides the global `github_pat` for that mapping -- use this when repositories belong to different owners or organisations (fine-grained PATs are scoped per owner). The global `github_pat` and `github_webhook_secret` are also `sensitive` type (encrypted at rest). `API::with_pat( $pat )` returns a cloned API instance with the override applied; `Import_Processor::api_for_mapping( $mapping )` selects the right instance automatically.
129129

130-
The `repo_name` field uses TomSelect autocomplete (`field_class: 'ts_autocomplete'` + `field_attributes` from `Settings::get_github_repo_search_attributes()`). The backend is `wp_ajax_wzkb_github_repo_search` (registered in `Settings::__construct()`), which queries `GET /search/repositories?q=` via the global PAT and returns `{ id: repo-name, name: owner/repo-name }` items. The `ts_autocomplete` class is picked up automatically by `includes/admin/settings/js/tom-select-init.js`, already enqueued by `Settings_API` on settings pages do not re-enqueue or re-implement TomSelect.
130+
The `repo_name` field uses TomSelect autocomplete (`field_class: 'ts_autocomplete'` + `field_attributes` from `Settings::get_github_repo_search_attributes()`). The backend is `wp_ajax_wzkb_github_repo_search` (registered in `Settings::__construct()`), which queries `GET /search/repositories?q=...` via the global PAT and returns `{ id: repo-name, name: owner/repo-name }` items. The `ts_autocomplete` class is picked up automatically by `includes/admin/settings/js/tom-select-init.js`, already enqueued by `Settings_API` on settings pages -- do not re-enqueue or re-implement TomSelect.
131131

132132
### Block Development
133133

134-
Blocks are in `includes/blocks/src/[block-name]/`. Each block has its own `block.json`, React `edit.js`, and server-side render via PHP. After editing block source, run `npm run build:[block-name]` never edit files in `build/` directly.
134+
Blocks are in `includes/blocks/src/[block-name]/`. Each block has its own `block.json`, React `edit.js`, and server-side render via PHP. After editing block source, run `npm run build:[block-name]` -- never edit files in `build/` directly.
135135

136136
### Public Helper Functions
137137

138138
`includes/functions.php` exposes the plugin's public API. Key functions:
139139

140-
- `wzkb_knowledge()` render the full KB output
141-
- `wzkb_get_option( $key )` / `wzkb_get_settings()` read settings (prefer over `get_option()` directly)
142-
- `wzkb_get_breadcrumb()`, `wzkb_get_search_form()`, `wzkb_get_alert()`, `wzkb_related_articles()` frontend rendering helpers
143-
- `wzkb_get_the_post_thumbnail()` thumbnail retrieval (supports ACF image fields)
144-
- `wzkb_get_kb_url()`, `wzkb_get_product_sections_list()`, `wzkb_get_term_hierarchy_path()` URL and taxonomy helpers
140+
- `wzkb_knowledge()` -- render the full KB output
141+
- `wzkb_get_option( $key )` / `wzkb_get_settings()` -- read settings (prefer over `get_option()` directly)
142+
- `wzkb_get_breadcrumb()`, `wzkb_get_search_form()`, `wzkb_get_alert()`, `wzkb_related_articles()` -- frontend rendering helpers
143+
- `wzkb_get_the_post_thumbnail()` -- thumbnail retrieval (supports ACF image fields)
144+
- `wzkb_get_kb_url()`, `wzkb_get_product_sections_list()`, `wzkb_get_term_hierarchy_path()` -- URL and taxonomy helpers
145145

146146
Settings are stored as a single serialized array under option key `wzkb_settings`. All settings filters use the prefix `wzkb_` (e.g. `wzkb_get_option_{$key}`).
147147

@@ -151,6 +151,6 @@ Endpoints under `/wzkb/v1/`: `/sections` (product sections), `/knowledgebase` (l
151151

152152
## Code Quality Configuration
153153

154-
- **PHPCS**: `phpcs.xml.dist` WordPress coding standards
155-
- **PHPStan**: `phpstan.neon.dist` Level 5 strict analysis; baseline in `phpstan-baseline.neon`; ACF Pro stubs included
156-
- **PHPUnit**: `phpunit.xml.dist` test configuration, tests in `phpunit/tests/`
154+
- **PHPCS**: `phpcs.xml.dist` -- WordPress coding standards
155+
- **PHPStan**: `phpstan.neon.dist` -- Level 5 strict analysis; baseline in `phpstan-baseline.neon`; ACF Pro stubs included
156+
- **PHPUnit**: `phpunit.xml.dist` -- test configuration, tests in `phpunit/tests/`

includes/admin/settings/class-settings-sanitize.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public function sanitize_sensitive_field( $value, $key ) {
283283
}
284284

285285
// If input is masked, return existing encrypted key.
286-
if ( strpos( (string) $value, '**' ) !== false ) {
286+
if ( is_string( $value ) && strpos( $value, '**' ) !== false ) {
287287
return $stored_encrypted_key;
288288
}
289289

0 commit comments

Comments
 (0)