Developer documentation

OFeedback v2.0.0

Self-hosted feedback collection for WordPress — floating widget, inbox workflow, analytics, screenshot capture, Slack notifications, and a clean developer API. Your data never leaves your server.

WordPress plugin WP 5.9 · PHP 7.4+ GPL-2.0+ v2.0.0
01 · Overview

What OFeedback does

A complete feedback loop in one plugin — collect, triage, notify, analyse, and export. No third-party service, no monthly fee, no data egress.

💬
Floating Widget
Fixed-position button opens a modal — 4 trigger modes
📥
Admin Inbox
New → Read → Archived status workflow with bulk actions
📊
Analytics
Type breakdown, rating distribution, 30-day trend chart
📸
Screenshot Capture
html2canvas on-demand — loaded only when the user clicks attach
📧
Email Notifications
Multiple recipients, optional submitter confirmation
💬
Slack Integration
POST to an Incoming Webhook on each new submission
📄
CSV Export
Filter-aware one-click download, Excel-compatible BOM
🔒
GDPR Toggle
Disable IP address storage with a single checkbox
🧹
Clean Uninstall
Drops table, options, cron events, and screenshot files on delete
02 · Requirements

What you need

WordPress
5.9+
PHP
7.4+
MySQL
5.7+
MariaDB 10.3+
Browser
Modern*
JS required
03 · Installation

Getting installed

Via ZIP upload (recommended)

1
Log into WordPress Admin → Plugins → Add New → Upload Plugin.
2
Choose ofeedback.zip and click Install Now.
3
Click Activate Plugin.
4
Navigate to OFeedback in the left-hand menu.

Via FTP / file manager

1
Unzip ofeedback.zip to get the ofeedback/ folder.
2
Upload the folder to wp-content/plugins/.
3
Activate via Plugins → Installed Plugins.

On activation

  • The wp_ofb_feedback database table is created (or upgraded via dbDelta if an older version exists).
  • A daily WP-Cron event (ofb_cleanup_screenshots) is scheduled to delete screenshot files from archived entries older than 30 days.
04 · Uninstallation

Clean uninstall

Deactivating the plugin does not delete your data. To remove everything permanently, deactivate first, then click Delete in Plugins.

WordPress calls uninstall.php automatically on delete, which:

1
Drops the wp_ofb_feedback table.
2
Deletes all ofb_* options from wp_options.
3
Clears the ofb_cleanup_screenshots cron event.
4
Deletes all screenshot files from wp-content/uploads/ofb-screenshots/.
⚠️This is permanent. Export your data to CSV first if you need to keep it — Inbox → CSV with all filters blank gives you everything.
05 · Admin Interface

Admin interface

A full-width 3-row sticky header — the WP sidebar is hidden on all OFeedback pages to prevent navigation conflicts.

RowContents
Brand barOFeedback logo + icon, version badge, user avatar, light/dark theme toggle
NavigationInbox · Analytics · Settings
Contextual barBreadcrumb + contextual actions / filters
5.1 · Inbox

Feedback inbox

The main submission table. Every piece of feedback flows through the NewReadArchived status lifecycle.

Status tabs

TabDescription
NewUnread submissions — left-border accent on each row
ReadViewed — status auto-sets to "read" on detail view open
ArchivedManually archived — screenshot files deleted 30 days after archiving
AllEvery submission regardless of status

Table columns

Checkbox · From (name + email) · Type tag · Message excerpt · Rating · Screenshot indicator · Page · Date · Status badge · Row actions

Filters & bulk actions

FeatureDetail
Type filterDropdown matching enabled feedback types
Date rangeFrom / To ISO date pickers
SearchFull-text across name, email, subject, message
CSV exportButton in topbar — downloads current type/status selection
Mark ReadBulk action on checked rows
ArchiveBulk action on checked rows
DeleteBulk action with confirmation prompt
5.2 · Detail View

Submission detail view

Click View on any inbox row to open the full submission. Opening a "new" entry automatically marks it "read".

CardContents
MessageType tag, status badge, star rating, subject, full message body
Admin NotesPrivate textarea saved per entry — never visible to submitters
Submitter sidebarName, email, user ID (if logged in), IP address
Context sidebarSubmission datetime, page title + URL, browser user agent
Reply cardMailto link pre-filled with submitter email and subject (shown only when email is present)
Screenshot cardThumbnail preview with download link; shows deletion date when archived
5.3 · Analytics

Analytics tab

Server-rendered — no additional API calls. Pure CSS height-percentage charts, no JavaScript library required.

Stat cards

CardData
Total SubmissionsAll-time count
This MonthCount since the 1st of the current month
Avg RatingAverage star rating across all rated submissions (0.0 if none)
UnreadCount with status = 'new'

Charts

  • Submissions by Type — horizontal bar chart per feedback type
  • Rating Distribution — bars for each star value (1–5)
  • Last 30 Days — vertical bar chart with one bar per day; missing days shown as zero
06 · Frontend Widget

Frontend widget

A fixed-position button built and appended to <body> via JavaScript on page load (or after a trigger event).

Widget structure

[Trigger button]
Fixed, bottom-right or bottom-left
[Panel — slides up on click]
Header — "Share Feedback" + close button
Type select
Subject input (optional)
Message textarea (required)
Screenshot attach button (if enabled)
Email input (optional)
Star rating
Footer — link to dedicated feedback page + Send Feedback button

Trigger modes

ModeBehaviour
ImmediatelyWidget appears as soon as the page loads
After a delayWidget appears N seconds after page load (configurable)
After scroll depthWidget appears after visitor scrolls X% of the page (5–95%)
Exit intentWidget appears when cursor leaves the top of the viewport

Auto-fill & exclusions

Logged-in users have their display_name and user_email pre-filled via wp_localize_script — no client-side fetch required.

Post/page IDs listed under Settings → Show/Hide Rules → Excluded Post IDs will not render the widget, even when globally enabled.

07 · Shortcode

[ofeedback] shortcode

Embeds the full feedback form on any page or post — independent of the floating widget setting.

[ofeedback]
[ofeedback title="Give Us Feedback" description="We read every submission."]
AttributeDefaultDescription
titleFeedbackPage heading
descriptionYour thoughts help us improve.Subheading text

The form includes: name, email, type, subject, message (required), screenshot, and star rating. Add [ofeedback] to as many pages as you like — each renders an independent form.

08 · Settings Reference

Settings reference

Option keyUI labelTypeDefault
ofb_notify_emailsNotification RecipientsTextareaSite admin email
ofb_send_confirmationSend confirmation email to submitterCheckboxoff
ofb_slack_webhookSlack Webhook URLURL
ofb_widget_enabledShow floating widgetCheckboxon
ofb_widget_positionButton PositionSelectright
ofb_widget_colorButton ColorColor + hex#38BDF8
ofb_widget_labelButton LabelTextFeedback
ofb_widget_triggerTrigger ModeSelectimmediate
ofb_widget_delay_secondsDelay (seconds)Number3
ofb_widget_scroll_pctScroll depth (%)Number 5–9550
ofb_enabled_typesEnabled Feedback TypesCheckboxesall
ofb_screenshot_enabledAllow screenshot attachmentCheckboxon
ofb_feedback_page_idLinked Feedback PagePage selector
ofb_store_ipStore IP addressCheckboxon
ofb_excluded_post_idsExcluded Page/Post IDsComma-separated IDs
09 · Notifications

Notifications overview

Every new submission triggers a single OFB_Mailer::send_all() call that fires the admin email, the optional Slack webhook, and the optional submitter confirmation in one shot.

9.1 · Email Notifications

Email notifications

On each new submission, an HTML email is sent to all configured notification recipients.

Recipients

Settings → Email Notifications → Notification Recipients. Enter one email per line, or comma-separated. Multiple recipients are supported.

Email contents

Type, name, email, subject, star rating, page link, full message body, and a View in OFeedback CTA button styled with the Orravo orange accent.

Submitter confirmation

When Send confirmation email to submitter is enabled and the submitter provides an email address, they receive a receipt email containing their own message.

9.2 · Slack Integration

Slack integration

Configure a Slack Incoming Webhook URL in Settings → Slack Integration. Leave the field blank to disable. Each new submission POSTs a formatted JSON payload to the webhook.

JSON{
  "text": "*[Site Name] New Feedback — Bug / Technical Issue ★★★☆☆*\n*From:* Jane Doe\n>Login page throws a 500 error after password reset…\n<https://example.com/wp-admin/admin.php?page=ofb-feedback&view=detail&id=42|View in OFeedback admin>"
}
10 · Screenshot Capture

Screenshot capture

Client-side capture using html2canvas — loaded from jsDelivr CDN on demand only when the user clicks "Attach Screenshot". The ~300 KB library never affects initial page load.

Capture process

1
User clicks Attach Screenshot — html2canvas loads on demand.
2
html2canvas renders the visible page to a <canvas>, excluding the widget panel itself.
3
Canvas is compressed to JPEG (quality 0.72, max dimension 1280px).
4
Base64 data URL is uploaded via AJAX to ofb_upload_screenshot.
5
Server validates magic bytes (JPEG or PNG only), enforces the 2 MB limit, saves to wp-content/uploads/ofb-screenshots/, and returns the public URL.
6
URL and server path are stored with the submission on form submit.

Cleanup & security

A daily WP-Cron job deletes screenshot files belonging to entries archived more than 30 days ago. Trigger manually at Settings → Screenshot Storage → Run Cleanup Now.

The ofb-screenshots/ directory is protected by a .htaccess file that disables directory listing and blocks PHP execution.

11 · CSV Export

CSV export

One-click download from the Inbox topbar — filtered to the current type and status selection. UTF-8 with BOM for Excel compatibility. Direct browser download, no AJAX, no page reload.

Export columns
ID, Date, Status, TypeSubject, Message, Name, Email
Rating, Page Title, Page URLIP Address, Admin Notes
💡Leave all filters blank (no type, no status selected) and click CSV to export every submission — useful before uninstalling.
12 · Developer API

Developer API

One action hook, one filter hook, a set of PHP query functions, and guidance for adding your own REST endpoint.

12.1 · Action Hooks

Action hooks

ofeedback_submission_created

Fires immediately after a new submission is saved to the database, after email and Slack notifications have been sent.

PHPadd_action( 'ofeedback_submission_created', function( int $id, array $data ) {
    // $id   — The new row ID in wp_ofb_feedback
    // $data — Sanitized submission array (type, message, name, email, rating, …)
    error_log( 'New feedback #' . $id . ' from ' . ( $data['name'] ?: 'Anonymous' ) );
}, 10, 2 );

// Zapier / Make integration example
add_action( 'ofeedback_submission_created', function( $id, $data ) {
    wp_remote_post( 'https://hooks.zapier.com/...', [
        'body'    => wp_json_encode( array_merge( ['id' => $id], $data ) ),
        'headers' => ['Content-Type' => 'application/json'],
    ]);
}, 10, 2 );
12.2 · Filter Hooks

Filter hooks

ofb_notify_email

Filters the notification recipient(s) before the admin email is sent. Receives the string of addresses (comma-separated or single).

PHPadd_filter( 'ofb_notify_email', function( string $email ): string {
    return 'feedback@example.com, team@example.com';
} );
12.3 · PHP Functions

PHP functions

OFB_DB::query()

PHP$submissions = OFB_DB::query([
    'status'    => 'new',         // '', 'new', 'read', 'archived'
    'type'      => 'bug',         // '', 'general', 'bug', 'suggestion', …
    'search'    => 'login page',  // full-text across name, email, subject, message
    'date_from' => '2025-01-01',  // Y-m-d
    'date_to'   => '2025-12-31',  // Y-m-d
    'orderby'   => 'created_at',  // id, name, email, type, rating, status, created_at
    'order'     => 'DESC',        // ASC or DESC
    'per_page'  => 20,
    'paged'     => 1,
]);
// Returns array of stdClass objects

OFB_DB::get()

PHP$entry = OFB_DB::get( 42 );
if ( $entry ) {
    echo $entry->name . ': ' . $entry->message;
}

Analytics methods

MethodReturns
OFB_DB::count_by_status( $status )Count of submissions matching status (blank = total)
OFB_DB::analytics_by_type()['bug' => 12, 'general' => 8, …] ordered by count desc
OFB_DB::analytics_by_rating()[1 => 2, 2 => 5, 3 => 10, 4 => 18, 5 => 6] — all five stars, 0 if none
OFB_DB::analytics_by_date( $days )['Y-m-d' => count, …] — last N days, missing dates = 0
OFB_DB::average_rating()Float — average star rating (0.0 if no rated submissions)
OFB_Submission::types()Full type list: ['general' => 'General Feedback', …]
OFB_Submission::enabled_types()Only types currently enabled in settings (falls back to all)
12.4 · REST Endpoint

REST endpoint

OFeedback does not ship a public REST endpoint by default. Add your own using the PHP functions above:

PHPadd_action( 'rest_api_init', function() {
    register_rest_route( 'ofeedback/v1', '/submissions', [
        'methods'  => 'GET',
        'callback' => function( WP_REST_Request $request ) {
            if ( ! current_user_can('manage_options') ) {
                return new WP_Error('forbidden', 'Insufficient permissions.', ['status' => 403]);
            }
            return rest_ensure_response( OFB_DB::query([
                'status'   => $request->get_param('status') ?? '',
                'per_page' => min( 100, absint( $request->get_param('per_page') ?? 20 ) ),
                'paged'    => absint( $request->get_param('page') ?? 1 ),
            ]));
        },
        'permission_callback' => '__return_true',
    ]);
});
13 · Database Schema

Database schema

Single table: wp_ofb_feedback (prefix varies). Indexes on status, type, archived_at, and created_at.

ColumnTypeDescription
idBIGINT UNSIGNED AUTO_INCREMENTPrimary key
typeVARCHAR(40)Feedback type: general, bug, suggestion, etc.
subjectVARCHAR(255)Optional subject line
messageTEXTThe feedback message (required)
ratingTINYINT(1) UNSIGNEDStar rating 1–5, or NULL
nameVARCHAR(150)Submitter name (optional)
emailVARCHAR(150)Submitter email (optional)
page_urlVARCHAR(500)URL of the page where feedback was submitted
page_titleVARCHAR(255)Title of that page
ip_addressVARCHAR(45)Submitter IP (empty when ofb_store_ip is off)
user_agentVARCHAR(500)Browser user agent string
user_idBIGINT UNSIGNEDWP user ID, or NULL for guests
statusVARCHAR(20)new, read, archived
admin_notesTEXTPrivate notes by admins
screenshot_urlVARCHAR(500)Public URL to screenshot file
screenshot_pathVARCHAR(500)Absolute filesystem path (used for cleanup)
archived_atDATETIMEWhen the entry was archived (for cleanup scheduling)
created_atDATETIMESubmission timestamp
updated_atDATETIMELast modification timestamp (auto-updated)
14 · Privacy & GDPR

Privacy & GDPR

Data collected per submission

  • Message (required)
  • Type, subject, rating (form fields)
  • Name, email (optional — submitter-provided)
  • Page URL and page title (from window.location and document.title)
  • IP address (optional — disable via Settings → Privacy)
  • Browser user agent string
  • WordPress user ID (if the submitter is logged in)

Compliance checklist

StepHow
Disable IP storageSettings → Privacy → uncheck Store IP address. Existing IPs are not retroactively deleted — export/delete first.
Privacy policy disclosureAdd OFeedback's data collection to your policy. Suggested text in the docs above.
Right to erasureDelete individual submissions from the Inbox. No automated erasure tool.
Data locationAll data stored in your own WordPress database. No data sent to Orravo or third parties (except optionally to Slack via the webhook you configure).
Screenshot filesStored in wp-content/uploads/ofb-screenshots/ on your server. Auto-deleted 30 days after archiving.
15 · Frequently Asked Questions

Frequently asked questions

Does OFeedback work without jQuery?
The frontend widget and admin both require jQuery. WordPress includes jQuery by default — OFeedback enqueues it as a dependency.
Can I style the widget to match my brand?
Yes. Set a hex colour in Settings → Widget Appearance → Button Color. For full CSS control, target .ofb-widget-trigger and .ofb-widget-panel.
Will the widget slow down my site?
The ofb-frontend-v2.js file is under 12 KB uncompressed. html2canvas (~300 KB) is only loaded when a user clicks "Attach Screenshot" — not part of the initial page load.
Can I use the form on multiple pages?
Yes. Add [ofeedback] to as many pages as you like — each renders an independent form.
What happens when a submission is archived?
Status changes to archived. The screenshot file (if any) is queued for deletion 30 days later. The submission data itself remains in the database until manually deleted.
Is there a submission limit?
No built-in limit. Rate limiting prevents the same IP from submitting more than once per 5 minutes.
Does it work with caching plugins?
Yes. AJAX endpoints bypass page caching. The wp_create_nonce call is inside wp_enqueue_scripts which runs per-request (not cached).
Can I integrate with Zapier or Make?
Use the ofeedback_submission_created action hook to POST to a webhook URL. See the Action Hooks section for the full example.
16 · Changelog

What's changed

v2.0.0 Current release
  • NewAnalytics tab — stat cards, type breakdown, rating distribution, 30-day daily trend (CSS-only charts)
  • NewSlack webhook integration — formatted JSON POST on each new submission
  • NewMultiple notification email recipients (comma-separated or one per line)
  • NewCSV export with date-range and type/status filtering
  • NewAdmin bar unread badge (count of new submissions)
  • NewWP admin sidebar hidden on plugin pages for clean full-width layout
  • NewLogged-in user auto-fill for widget and page form
  • NewWidget trigger modes: immediate, delay, scroll depth, exit intent
  • NewEnabled types setting — restrict which feedback types appear in the form
  • Newofb_excluded_post_ids — hide widget on specific pages
  • Newofb_store_ip — GDPR toggle for IP address storage
  • Newuninstall.php — clean table drop + options + cron + screenshot folder removal
  • Newofeedback_submission_created action hook
  • NewOFB_DB::analytics_by_type/rating/date(), average_rating(), count_this_month()
  • ImprovedOFB_DB::query() + query_count() accept date_from / date_to params
  • ImprovedMailer uses OFB_Mailer::send_all() — single call fires email + Slack + confirmation
  • ImprovedAdmin notification email uses Orravo-branded HTML (orange accent)
  • FixedWP admin sidebar interference with plugin navigation
v1.2.0 Previous release
  • NewRebranded from fideograph-feedback to OFeedback
  • NewStandalone WP menu via add_menu_page()
  • New3-row sticky header (brand bar + topnav + topbar)
  • NewBulk actions: mark read, archive, delete
  • NewDate filter and search in inbox
  • NewAdmin notes per submission
  • Newhtml2canvas client-side screenshot capture (replaced cron-based approach)
  • NewScreenshot upload with magic-byte validation, 2 MB ceiling
  • NewRate limiting (1 per IP per 5 min)
  • NewDark/light theme toggle with localStorage persistence
  • ImprovedStatus auto-set to "read" on detail view open
  • NewSubmission confirmation email to submitter (optional)
v1.1.0 Initial release (from fideograph-feedback source)
✦ Need help?

Got a question about OFeedback?

Reach out directly — Kenneth replies within 24 hours.