# Monetize Me 3.0.2

Monetize Me is a modular WordPress advertising manager for trusted Google AdSense and third-party monetization code.

## Requirements

- WordPress 5.8 or later
- PHP 7.4 or later
- WordPress Multisite is supported through network-wide settings

## Installation and activation

### Single site

1. Upload the `monetize-me` directory to `wp-content/plugins/`, or upload the release ZIP from **Plugins → Add New → Upload Plugin**.
2. Activate **Monetize Me**.
3. Open the top-level **Monetize Me** admin menu.
4. Create or review categories and sponsors, then create an enabled ad.
5. Use the shortcode, Advertisement block, theme hooks, or automatic placements.

### Multisite

1. Install the plugin from Network Admin or upload it to `wp-content/plugins/`.
2. Network activate **Monetize Me**.
3. Manage all settings from **Network Admin → Monetize Me**.
4. Individual sites do not receive site-level settings pages. One network option controls the shared ad library and placements.

## Architecture

The main plugin file defines release constants, registers activation, and boots one orchestrator. Feature code is isolated under `includes/features/`:

- `categories/`: category normalization and public lookup APIs
- `sponsors/`: sponsor normalization and public lookup APIs
- `ads/`: ad normalization, matching, random selection, and rendering
- `admin/`: capability-protected CRUD screens, nonce-protected save handlers, views, and admin assets
- `shortcode/`: `[mmps]` registration and attribute normalization
- `block/`: dynamic Advertisement block, source code, compiled editor assets, and server rendering
- `frontend/`: optional frontend stylesheet registration and enqueue logic
- `placements/`: theme action mappings, archive intervals, post-content insertion, and placement APIs

## Data storage

- Single site: `wp_options` key `monetize_me_settings`
- Multisite: `wp_sitemeta` network option `monetize_me_settings`
- No custom post type or taxonomy is registered
- Current settings schema: `3`

## Data retention

Deactivating or deleting Monetize Me does not remove the `monetize_me_settings` option. Existing categories, sponsors, ads, and placement configuration remain available when the plugin is activated or installed again.

## Starter data

New installations receive:

- Header Ads (`header-ads`)
- Footer Ads (`footer-ads`)
- In-article Ads (`in-article-ads`)
- Sidebar Ads (`sidebar-ads`)
- General sponsor (`general`)

Stable internal IDs keep ad and placement references intact when names or slugs are edited.

## Shortcode

```text
[mmps adcategory="in-article-ads" adsponsor="general" classname="article-ad"]
```

All attributes are optional. The shortcode always renders one randomly selected enabled ad that matches the supplied category and sponsor.

## Gutenberg block

The dynamic `monetize-me/ad` block exposes:

- Ad Category
- Ad Sponsor
- Extra CSS Class

It executes no third-party ad code in the editor. PHP renders one random matching ad on the frontend.

## Theme hooks

Add supported actions to a theme or child theme and map each hook from **Monetize Me → Placements**. Pass an optional space-separated CSS class string to add classes to the outer `.monetize-me` container:

```php
do_action( 'monetize_me_after_header', 'site-header-ad' );
do_action( 'monetize_me_before_footer', 'site-footer-ad' );
do_action( 'monetize_me_before_archive_start', 'archive-start-ad' );
do_action( 'monetize_me_after_archive_end', 'archive-end-ad' );
```

For archive items, pass a one-based position:

```php
global $wp_query;

$position = $wp_query->current_post + 1;

do_action(
    'monetize_me_between_archive_item',
    get_post(),
    $position,
    'archive-list-ad'
);
```

The archive callback renders only when Archive Loop Ads are enabled and the item position matches the configured random interval or exact-position rule. Its optional third argument supplies CSS classes for the outer ad container.

The corresponding public placement functions also accept an optional third `$classname` argument:

```php
monetize_me_get_placement_ad( $hook_name, $context, $classname );
monetize_me_render_placement( $hook_name, $context, $classname );
```

## Public rendering API

```php
$ad_html = monetize_me_render_ad(
    'in-article-ads',
    'general',
    'custom-theme-ad',
    array(
        'source' => 'theme_template',
    )
);

echo $ad_html; // Intentionally contains trusted script, iframe, or HTML ad code.
```

Useful APIs include:

- `monetize_me_get_categories()`
- `monetize_me_get_category( $identifier )`
- `monetize_me_get_sponsors()`
- `monetize_me_get_sponsor( $identifier )`
- `monetize_me_get_ads( $enabled_only = false )`
- `monetize_me_get_matching_ads( $category, $sponsor )`
- `monetize_me_select_ad( $category, $sponsor, $context )`
- `monetize_me_render_ad( $category, $sponsor, $classname, $context )`
- `monetize_me_get_placement_settings()`
- `monetize_me_get_content_post_type_options()`
- `monetize_me_settings_page_url()`

## Frontend output

Every rendering path uses the same structure:

```html
<div class="monetize-me category-slug">
    <div class="ad-wrapper">
        <!-- selected ad code -->
    </div>
</div>
```

The optional stylesheet is located at `assets/css/frontend.css` and can be disabled from **Monetize Me → Placements → Frontend Styles**.

## Raw ad-code policy

Monetization code is intentionally stored and rendered without KSES filtering, HTML escaping, `wpautop()`, `nl2br()`, or automatic `<br>` insertion. This is required for provider scripts and iframes.

Every settings write requires the plugin management capability and a dedicated verified nonce. On Multisite, only network administrators with `manage_network_options` can manage the shared library.

Only paste code from a trusted provider or advertiser.

## Release review notes

Version 3.0.2 includes:

- Correct plugin and block version metadata
- Gutenberg Block API version 3 for iframe editor compatibility
- Direct nonce verification in every save handler before request data is read
- One-time request unslashing before feature-specific sanitization
- WordPress-prefixed variables in all included admin templates
- WordPress.org automatic translation loading without a manual textdomain loader
- Correct HTTP 403 handling for unauthorized admin requests
- Dedicated nonces for ads, categories, sponsors, and placement forms
- Page-scoped admin CSS and JavaScript
- Optional frontend CSS loaded through `wp_enqueue_style()`
- Registered block assets with declared WordPress dependencies
- JSON-encoded editor configuration loaded before the block script
- Verbatim ad-code storage and raw frontend output
- Robust filtered custom theme-hook defaults
- Persistent settings across plugin deactivation, deletion, reinstallation, and reactivation
- GPLv2 license file
- Repository-formatted `readme.txt` with activation, integration, API, Multisite, and security guidance
