# Creating a New Pro Module

This repository uses a legacy module loader plus requirement classes. A new Pro checklist module is usually made of:

- a module bootstrap file in `src/modules/<module-slug>/<module-slug>.php`
- one or more requirement classes in `src/modules/<module-slug>/lib/Requirement/`
- an optional meta-box script in `src/modules/<module-slug>/assets/js/meta-box.js`

Use `src/modules/prohibited-words/` or `src/modules/no-heading-tags/` as the reference shape.

## 1. Create the module folder

Create:

```text
src/modules/<module-slug>/
src/modules/<module-slug>/<module-slug>.php
src/modules/<module-slug>/lib/Requirement/<RequirementClass>.php
src/modules/<module-slug>/assets/js/meta-box.js      # optional
```

Example:

```text
src/modules/prohibited-words/
src/modules/prohibited-words/prohibited-words.php
src/modules/prohibited-words/lib/Requirement/ProhibitedWords.php
src/modules/prohibited-words/assets/js/meta-box.js
```

## 2. Add the module bootstrap class

In `src/modules/<module-slug>/<module-slug>.php`:

- define the legacy module class as `PPCH_<Studly_Name>`
- extend `PublishPress\Checklists\Core\Legacy\Module`
- set `public $module_name` to the underscore version, for example `prohibited_words`
- register the module with `$legacyPlugin->register_module(...)`
- hook `publishpress_checklists_load_addons`
- in `actionLoadAddons()`, add:
  - `FILTER_POST_TYPE_REQUIREMENTS`
  - `ACTION_CHECKLIST_ENQUEUE_SCRIPTS` if the requirement needs editor-side live updates
- in `filterPostTypeRequirements()`, append the requirement class name

Important naming rules:

- folder/file slug: kebab-case, for example `prohibited-words`
- module name / option namespace: snake_case, for example `prohibited_words`
- requirement class namespace: `PublishPress\ChecklistsPro\<ModuleNamespace>\Requirement`

## 3. Add the requirement class

Create `src/modules/<module-slug>/lib/Requirement/<RequirementClass>.php`.

Usually this class:

- extends one of the base classes from the bundled free plugin:
  - `Base_simple`
  - `Base_multiple`
  - `Base_counter`
  - `Base_time`
- defines:
  - `public $name`
  - `public $group`
  - `public $position`
- implements:
  - `init_language()`
  - `get_current_status()`
- optionally overrides:
  - `filter_requirements_list()`
  - `get_setting_field_html()`
  - `filter_settings_validate()`

### Group values

Use an existing tab key unless you are intentionally adding a new tab. Common examples:

- `content`
- `categories`
- `tags`
- `images`
- `featured_image`
- `approval`
- `accessibility`
- `publish_date_time`
- `rank_math`
- `all_in_one_seo`
- `audio_video`
- `woocommerce`

## 4. Register the namespace in the plugin bootstrap

Update [publishpress-checklists-pro.php](/Users/rizaardiyanto/Cove/Sites/ppchecklists.localhost/public/wp-content/plugins/publishpress-checklists-pro/publishpress-checklists-pro.php) and add:

```php
Autoloader::addNamespace(
    'PublishPress\\ChecklistsPro\\<ModuleNamespace>\\',
    __DIR__ . '/src/modules/<module-slug>/lib/'
);
```

Without this, the requirement class will not autoload.

## 5. Register the module directory

Update [src/PluginInitializer.php](/Users/rizaardiyanto/Cove/Sites/ppchecklists.localhost/public/wp-content/plugins/publishpress-checklists-pro/src/PluginInitializer.php) in `filterModulesDirs()`:

```php
$dirs['<module-slug>'] = rtrim($this->modulesDirPath, DIRECTORY_SEPARATOR);
```

Without this, the legacy loader will not include `src/modules/<module-slug>/<module-slug>.php`.

## 6. Decide if it needs a meta-box script

Create `assets/js/meta-box.js` and enqueue it from the module bootstrap if the requirement must react live in:

- Gutenberg
- Classic editor
- custom editor UI states

Typical cases:

- scanning post content
- validating headings, images, links, words, phrases
- syncing metabox status without saving

Skip the JS file if the requirement is only checked server-side on refresh/save.

## 7. Add static Pro requirement metadata when needed

Update [lib/vendor/publishpress/publishpress-checklists/modules/checklists/pro-requirements.php](/Users/rizaardiyanto/Cove/Sites/ppchecklists.localhost/public/wp-content/plugins/publishpress-checklists-pro/lib/vendor/publishpress/publishpress-checklists/modules/checklists/pro-requirements.php) when the requirement should appear in the Pro requirement catalog.

Add an entry with:

- `id`
- `type`
- `support`
- `group`
- `label`

This file is part of the shared checklist UI metadata. Keep the `id` aligned with the requirement `name`.

## 8. Add duplicate-checklist mappings if the requirement can be duplicated

Update [src/duplicate-checklist/lib/RequirementMappingData.php](/Users/rizaardiyanto/Cove/Sites/ppchecklists.localhost/public/wp-content/plugins/publishpress-checklists-pro/src/duplicate-checklist/lib/RequirementMappingData.php):

- `TITLE_MAPPING`
- `CLASS_MAPPING`
- `GROUP_MAPPING`
- `POSITION_MAPPING`

If the requirement stores custom setting suffixes beyond the existing defaults, also update `SETTING_SUFFIXES`.

If you skip this step, duplicate checklist support may fail or produce incomplete copies.

## 9. Add uninstall cleanup

Update [uninstall.php](/Users/rizaardiyanto/Cove/Sites/ppchecklists.localhost/public/wp-content/plugins/publishpress-checklists-pro/uninstall.php) and add the module option name:

```php
publishpress_checklists_<module_name>_options
```

Example:

```php
publishpress_checklists_prohibited_words_options
```

## 10. Choose a safe position

Each requirement has `public $position`, but duplicate values are risky because the current loader sorts by position and can collide.

Practical guidance:

- check existing positions before choosing a new one
- keep related requirements grouped near similar items
- use a free number, not one already used by another requirement

Quick search:

```bash
rg -n "public \\$position =" src lib/vendor/publishpress/publishpress-checklists
```

## 11. Verify the module

Minimum checks:

```bash
php -l src/modules/<module-slug>/<module-slug>.php
php -l src/modules/<module-slug>/lib/Requirement/<RequirementClass>.php
php -l publishpress-checklists-pro.php
php -l src/PluginInitializer.php
php -l src/duplicate-checklist/lib/RequirementMappingData.php   # if changed
php -l uninstall.php                                            # if changed
```

Manual checks in WordPress:

- requirement appears in the expected tab on the checklist settings screen
- settings save correctly
- metabox / Gutenberg panel status updates correctly
- duplicate checklist still works if mapped
- uninstall option list includes the module option

## Checklist

Use this as the minimum implementation checklist for a new Pro module:

- create `src/modules/<module-slug>/`
- create module bootstrap file
- create requirement class
- add autoloader namespace in `publishpress-checklists-pro.php`
- add module dir in `src/PluginInitializer.php`
- add meta-box JS and enqueue it if live editor behavior is needed
- add `pro-requirements.php` entry if the shared Pro catalog should know about it
- add `RequirementMappingData.php` entries if duplicate-checklist should support it
- add uninstall cleanup entry
- choose a unique `position`
- run `php -l` on touched PHP files

## Current reference modules

Good examples in this repo:

- `src/modules/no-heading-tags/`
- `src/modules/prohibited-words/`
- `src/modules/image-count/`
- `src/modules/approved-by-user/`

Use the smallest existing module that matches the requirement type you need.
