Programmatic Usage
NotedWP exposes several public PHP classes you can use directly in your theme or plugin code. This page covers the most useful methods for license checks, plan-gated features, limit enforcement, and custom capabilities.
Always wrap NotedWP calls in a class existence check to prevent fatal errors if the plugin is deactivated:
if (!class_exists('Noted_License')) {
return;
}
Noted_License
The Noted_License class handles plan detection and feature gating. All methods are static.
Noted_License::get_plan()
Returns the active plan as a string.
Returns: string (one of 'free', 'pro', 'agency', or 'm13_client')
$plan = Noted_License::get_plan();
// "free", "pro", "agency", or "m13_client"
Plan resolution order:
- If an M13 client token is present (
noted_m13_client_tokenoption), the plan resolves tom13_client. - If a valid license is active (
noted_license_statusisactive), the plan resolves to the stored tier (prooragency). - Otherwise, falls back to
free.
Noted_License::can(string $feature)
Check whether the current plan includes a specific feature.
Parameters:
$feature(string) – Feature key to check.
Returns: bool
if (Noted_License::can('drawing_tools')) {
// Enable annotation toolbar.
}
if (Noted_License::can('export_integrations')) {
// Show Trello/Asana/Jira export options.
}
Available feature keys:
| Feature Key | Free | Pro | Agency |
|---|---|---|---|
all_pages |
No | Yes | Yes |
unlimited_pins |
No | Yes | Yes |
unlimited_guests |
No | Yes | Yes |
drawing_tools |
No | Yes | Yes |
text_edits |
No | Yes | Yes |
priority_labels |
No | Yes | Yes |
internal_pins |
No | Yes | Yes |
bulk_resolve |
No | Yes | Yes |
password_links |
No | Yes | Yes |
link_expiry |
No | Yes | Yes |
keyboard_shortcuts |
No | Yes | Yes |
export_csv |
Yes | Yes | Yes |
export_integrations |
No | Yes | Yes |
export_task_mgmt |
No | Yes | Yes |
export_m13 |
No | Yes | Yes |
export_advanced |
No | No | Yes |
export_webhook |
No | No | Yes |
custom_branding |
No | Yes | Yes |
password_protect |
No | Yes | Yes |
expiry_links |
No | Yes | Yes |
white_label |
No | No | Yes |
multisite |
No | No | Yes |
The m13_client plan has the same features as agency.
Noted_License::is_tier(string $tier)
Check if the current plan matches a specific tier exactly.
Parameters:
$tier(string) – One of'free','pro','agency','m13_client'.
Returns: bool
if (Noted_License::is_tier('agency')) {
// Show agency-only branding options.
}
Noted_License::is_at_least(string $min_plan)
Check if the current plan meets or exceeds a minimum tier. The hierarchy is: free (0) < pro (1) < agency (2). The m13_client plan is treated as equivalent to agency (tier 2).
Parameters:
$min_plan(string) – Minimum plan required.
Returns: bool
if (Noted_License::is_at_least('pro')) {
// Any paid plan (pro, agency, m13_client) passes this check.
}
Noted_License::is_m13_managed()
Check if the site is managed by Mountain Thirteen Media via the M13 client token system.
Returns: bool
if (Noted_License::is_m13_managed()) {
// Adjust UI for M13-managed sites.
}
Noted_Limits
The Noted_Limits class enforces free-tier restrictions. On paid plans, these methods generally return true or null (unlimited). All methods are static.
Constants
Noted_Limits::FREE_PIN_LIMIT // 25
Noted_Limits::FREE_GUEST_LIMIT // 3
Noted_Limits::can_review_page(string $path)
Check if the overlay should load on a given page path. On the free tier, only the homepage (/) is allowed. Pro and Agency plans allow all pages.
Parameters:
$path(string) – The relative URL path (e.g.,/about/).
Returns: bool
$path = wp_make_link_relative(get_permalink());
if (Noted_Limits::can_review_page($path)) {
// Overlay will load on this page.
}
Noted_Limits::can_create_pin()
Check if the site has room for more pins. Free tier is capped at 25 total pins. Paid plans are unlimited.
Returns: bool
if (!Noted_Limits::can_create_pin()) {
wp_die('Pin limit reached. Upgrade to Pro for unlimited pins.');
}
Noted_Limits::can_add_guest(int $project_id)
Check if a project can accept more guest reviewers. Free tier is capped at 3 guests per project.
Parameters:
$project_id(int) – The project ID.
Returns: bool
if (Noted_Limits::can_add_guest($project_id)) {
// Allow guest registration.
} else {
// Show upgrade prompt.
}
Noted_Limits::get_remaining_pins()
Get the number of pins remaining before hitting the free tier cap. Returns null on paid plans (unlimited).
Returns: ?int
$remaining = Noted_Limits::get_remaining_pins();
if ($remaining === null) {
echo 'Unlimited pins available.';
} else {
echo "{$remaining} pins remaining on free plan.";
}
Noted_Limits::get_remaining_guests(int $project_id)
Get the number of guest slots remaining for a project on the free tier. Returns null on paid plans.
Parameters:
$project_id(int) – The project ID.
Returns: ?int
$remaining = Noted_Limits::get_remaining_guests($project_id);
Noted_Limits::can_use_feature(string $feature)
Convenience wrapper that calls Noted_License::can($feature) internally.
Parameters:
$feature(string) – Feature key (see the feature table above).
Returns: bool
if (Noted_Limits::can_use_feature('text_edits')) {
// Enable the text edit tool in your custom UI.
}
Noted_Capabilities
The Noted_Capabilities class defines the four custom WordPress capabilities used by NotedWP. These are string constants you can reference when checking permissions.
Constants
Noted_Capabilities::MANAGE_REVIEWS // 'noted_manage_reviews'
Noted_Capabilities::VIEW_REVIEWS // 'noted_view_reviews'
Noted_Capabilities::RESOLVE_PINS // 'noted_resolve_pins'
Noted_Capabilities::EXPORT_REVIEWS // 'noted_export_reviews'
Default Role Assignments
| Capability | Administrator | Editor |
|---|---|---|
noted_manage_reviews |
Yes | No |
noted_view_reviews |
Yes | Yes |
noted_resolve_pins |
Yes | Yes |
noted_export_reviews |
Yes | No |
Granting Capabilities to Other Roles
You can grant NotedWP capabilities to additional roles (e.g., author) using standard WordPress functions:
add_action('admin_init', function () {
$author = get_role('author');
if ($author) {
$author->add_cap(Noted_Capabilities::VIEW_REVIEWS);
$author->add_cap(Noted_Capabilities::RESOLVE_PINS);
}
});
Important. add_cap() writes to the database. Run this once (e.g., on plugin activation or via a one-time admin action), not on every page load.
Checking Capabilities
Use current_user_can() with the constants:
if (current_user_can(Noted_Capabilities::MANAGE_REVIEWS)) {
// User can manage pins, settings, and review data.
}
if (current_user_can(Noted_Capabilities::EXPORT_REVIEWS)) {
// User can export pins to external services.
}
Practical Examples
Gate a Feature Behind Plan Level
Show or hide a custom settings section based on the active NotedWP plan:
add_action('admin_menu', function () {
if (!class_exists('Noted_License')) {
return;
}
if (Noted_License::is_at_least('pro')) {
add_submenu_page(
'noted',
'Advanced Feedback Settings',
'Advanced',
Noted_Capabilities::MANAGE_REVIEWS,
'noted-advanced',
'render_advanced_settings_page'
);
}
});
Send a Custom Email When a Pin is Created
add_action('noted_pin_created', function (int $pin_id, array $pin_data) {
if (!class_exists('Noted_License')) {
return;
}
$page_url = home_url($pin_data['page_path'] ?? '/');
$admin_url = admin_url("admin.php?page=noted-pins&action=view&pin={$pin_id}");
$subject = sprintf('[NotedWP] New pin #%d on %s', $pin_data['pin_number'] ?? $pin_id, $page_url);
$body = sprintf(
"A new feedback pin was placed on %s.nnComment: %snnPriority: %snBreakpoint: %snnView in admin: %s",
$page_url,
$pin_data['body'] ?? '(no comment)',
$pin_data['priority'] ?? 'normal',
$pin_data['breakpoint'] ?? 'desktop',
$admin_url
);
wp_mail('[email protected]', $subject, $body);
}, 10, 2);
Query Pins via $wpdb for a Custom Report
Build a summary of open feedback grouped by page:
function get_noted_feedback_summary(): array {
global $wpdb;
$prefix = $wpdb->prefix;
$results = $wpdb->get_results(
"SELECT
p.url_path,
p.title,
COUNT(CASE WHEN pin.status = 'open' THEN 1 END) AS open_count,
COUNT(CASE WHEN pin.status = 'resolved' THEN 1 END) AS resolved_count,
MAX(pin.created_at) AS latest_pin
FROM {$prefix}noted_pages p
LEFT JOIN {$prefix}noted_pins pin ON pin.page_id = p.id
GROUP BY p.id
ORDER BY open_count DESC",
ARRAY_A
);
return $results;
}
// Usage:
$summary = get_noted_feedback_summary();
foreach ($summary as $row) {
printf(
"%s: %d open, %d resolvedn",
$row['title'] ?? $row['url_path'],
$row['open_count'],
$row['resolved_count']
);
}
Display a Pin Count Badge in Your Theme
Show the number of open feedback items for the current page:
function noted_pin_badge(): string {
if (!class_exists('Noted_Capabilities') || !current_user_can(Noted_Capabilities::VIEW_REVIEWS)) {
return '';
}
global $wpdb;
$path = wp_make_link_relative(get_permalink());
$count = (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*)
FROM {$wpdb->prefix}noted_pins pin
INNER JOIN {$wpdb->prefix}noted_pages page ON page.id = pin.page_id
WHERE page.url_path = %s AND pin.status = 'open'",
$path
));
if ($count === 0) {
return '';
}
return sprintf(
'<span class="noted-badge" style="background:#E06042;color:#fff;padding:2px 8px;border-radius:12px;font-size:12px;">%d open</span>',
$count
);
}
Use it in a template:
<h1><?php the_title(); ?> <?php echo noted_pin_badge(); ?></h1>
Check Limits Before Custom Pin Creation
If you are programmatically creating pins (outside of the REST API), check limits first:
function my_custom_create_pin(array $pin_data): ?int {
if (!class_exists('Noted_Limits')) {
return null;
}
if (!Noted_Limits::can_create_pin()) {
error_log('NotedWP pin limit reached.');
return null;
}
global $wpdb;
$wpdb->insert($wpdb->prefix . 'noted_pins', [
'page_id' => $pin_data['page_id'],
'project_id' => $pin_data['project_id'],
'x_percent' => $pin_data['x_percent'],
'y_percent' => $pin_data['y_percent'],
'pin_number' => $pin_data['pin_number'],
'status' => 'open',
'priority' => $pin_data['priority'] ?? 'normal',
'author_type' => 'user',
'author_user_id' => get_current_user_id(),
'body' => $pin_data['body'] ?? '',
'breakpoint' => $pin_data['breakpoint'] ?? 'desktop',
]);
$pin_id = (int) $wpdb->insert_id;
// Fire the same hook the REST API fires, so notifications still work.
do_action('noted_pin_created', $pin_id, $pin_data);
return $pin_id;
}
Note. When creating pins programmatically, remember to fire noted_pin_created yourself. The hook only fires automatically from the REST API. The same applies to noted_comment_created and noted_pin_resolved.