OCodeInsert v2.0.0
Inject custom HTML, JavaScript, CSS, and PHP snippets across 11 locations without touching theme files. Snippets survive theme updates, target by page type, URL, role, device, and schedule, with revision history, the OIntel linter, and Safe Mode auto-recovery.
What OCodeInsert does
A lightweight alternative to editing functions.php or installing bloated tools like WPCode. Built for inserting snippets that survive theme updates, stay organised, and recover automatically when something breaks.
unfiltered_html + Pro license at execution)?oci_safe=1 URL bypass (admin only), shutdown handler that auto-pauses the offending snippets, and a one-time recovery URL tokenOCI_Linter)libraries/snippets.json with {{PLACEHOLDER}} substitution via OCI_Librarywp-codemirroroci_snippets, oci_snippet_revisions (capped 25 per snippet), oci_execution_log (capped 10,000 rows)functions.php, header.php, bloated alternatives like WPCode / Insert Headers and Footers, or relying on theme option panels for script injection.Getting installed
From WordPress admin
ocodeinsert.zip and click Install Now, then Activate.Manual (FTP / SFTP)
ocodeinsert.zip.ocodeinsert/ folder to /wp-content/plugins/.oci_snippets, oci_snippet_revisions, oci_execution_log) and writes default settings to wp_options. v1.x sites upgrading to v2 are migrated automatically; the legacy options-blob payload is kept as oci_code_snippets_v1_backup.Up and running in minutes
Adding your first snippet: a Google Analytics 4 script injected into every page's <head>.
The script now appears inside <head> on every page, automatically wrapped in <script> tags.
Types, locations & targeting
Snippet types
| Type | Badge | Auto-wrap behaviour |
|---|---|---|
html | HTML | Raw output (no wrapping applied) |
js | JavaScript | Wraps bare JS in <script>…</script>. If code already contains <script, skipped. |
css | CSS | Wraps bare CSS in <style>…</style>. If code already contains <style, skipped. |
php | PHP · PRO | Captured via ob_start() and executed with eval('?>'.$code). Save requires unfiltered_html; execution additionally requires oci_is_pro(). |
The Type field drives syntax highlighting, auto-wrap logic, and the visual badge in the list. Where the snippet runs is controlled by Location.
Injection locations (11 + 7 OTheme)
<head></body>. Chat widgets, deferred scripts.wp_footer for page builders.<p> in article body.</p> via preg_replace_callback. N=0 means last paragraph.wp-login.php.wp-admin.ORRAVO_THEME_VERSION or template name), 7 extra hook locations open up: orravo_header_start, orravo_header_end, orravo_before_content, orravo_after_content, orravo_sidebar, orravo_footer, orravo_mobile_nav.the_content filter, OCodeInsert fires a JS fallback via wp_footer that finds the first and last <p> in the article body.Page type targeting
| Value | Matches |
|---|---|
all | Every page (default); overrides all others when selected |
front | Front page (is_front_page()) |
single | Single posts, pages, CPTs (is_singular()) |
archive | Category, tag, date archives + blog index |
search | Search results (is_search()) |
404 | 404 error page (is_404()) |
URL pattern targeting
Comma-separated URL paths relative to the domain root. Matched against $_SERVER['REQUEST_URI']. Case-insensitive, trailing slashes ignored. Applied in addition to Page Type; both must pass.
/about, /blog/*, /shop/product-category/*
| Pattern | Matches |
|---|---|
/about | /about and /about/ only |
/blog/* | /blog/, /blog/my-post, /blog/2024/01/post |
/shop/*, /cart | Any URL under /shop/ plus exactly /cart |
User condition, role, device, schedule, status, priority
| Setting | Value | Behaviour |
|---|---|---|
| User condition | all | All visitors (default) |
logged_in | Logged-in users only | |
logged_out | Logged-out visitors only | |
| User role | WordPress role slug | administrator, editor, author, etc. all disables the check. |
| Device | all | All devices (default) |
mobile | Mobile only (via wp_is_mobile()) | |
desktop | Desktop only (negated wp_is_mobile()) | |
| Schedule start | Y-m-d H:i:s | Snippet does not fire before this timestamp. Empty = no start. |
| Schedule end | Y-m-d H:i:s | Snippet does not fire after this timestamp. Empty = no end. |
| Status | published | Active and firing |
draft | Visible in admin, never fires | |
paused | Temporarily disabled (used by auto-pause on PHP fatal) | |
archived | Hidden from default list view | |
| Priority | integer | Controls injection order within the same location. Lower = earlier. Default: 10. |
| Paragraph N | integer ≥ 1, or 0 | Used by after_paragraph location. 0 means insert after the last paragraph. |
Admin interface
Navigate to OCodeInsert in the WordPress sidebar. A single menu entry, with no submenu interference.
Snippets list columns
| Column | Description |
|---|---|
| Status | Toggle Active (green) / Paused (grey) without reloading the page |
| Title | Snippet name |
| Type | HTML JavaScript CSS PHP badge |
| Location | Injection point |
| Targeting | Page type + user condition summary |
| Priority | Numeric priority within the location |
| Actions | Edit · Duplicate · Export JSON · Delete |
The filter toolbar supports live search by title (client-side) and filtering by Type, Status, and Location.
Snippet editor
Click + New Snippet or the edit icon on any row. The modal contains: Basic Info (title, type, location, priority, status), CodeMirror code editor (mode switches on type change), Targeting (page types, URL patterns, user condition, user role, device, schedule start/end, paragraph N), and the OIntel linter panel which surfaces errors and warnings before save. The snippet library is also reachable from the editor for placeholder-driven imports.
Duplicate & export
Duplicate: Click the copy icon in Actions. The copy gets a new UUID, (Copy) appended to the title, and is saved with status='draft'.
Export single snippet Free: Click the download icon to export a snippet as .json, generated client-side.
JSON{
"id": "...",
"title": "My Snippet",
"type": "js",
"code": "console.log('hello');",
"location": "head",
"status": "published",
"priority": 10,
"page_types": ["all"],
"pages": "",
"user_condition": "all",
"user_role": "all",
"device": "all",
"schedule_start": "",
"schedule_end": "",
"paragraph_num": 1,
"created": "2026-01-01 12:00:00",
"updated": "2026-01-01 12:00:00"
}
Settings page
Navigate to OCodeInsert → Settings via the top navigation tabs.
Global enable / disable
Master switch for the entire plugin. When disabled: no snippets are injected anywhere, the admin interface remains accessible, and all snippet data is preserved. Useful for temporary maintenance or debugging without deactivating the plugin.
Role permissions
Choose which WordPress roles can access the OCodeInsert admin panel. Administrators always have access. Default: Administrators only. Adding Editor lets editors manage snippets without full admin access.
Error log
Maintains an internal log of issues during snippet processing in the oci_error_log option (capped at 500 entries; oldest are sliced off). Columns: Time, Snippet ID, Message. Click Clear Log to delete all entries. Auto-recovery entries are tagged with snippet_id = system.
Safe mode
Safe mode suspends all snippet output globally to protect your site when a snippet causes unexpected issues.
Activating safe mode
| Method | Effect |
|---|---|
| Admin toggle | OCodeInsert → Settings → enable Safe Mode → Save. Affects all visitors. The admin bar shows an amber ⚠ OCI Safe Mode indicator. |
| URL parameter (admin only) | Append ?oci_safe=1 to any URL. The check requires current_user_can('manage_options'), so the bypass only works for logged-in admins; regular visitors with the parameter are unaffected. |
| Auto (shutdown handler) | OCodeInsert registers register_shutdown_function. If a PHP fatal (E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR) occurs on a request where snippets fired, Safe Mode is enabled with safe_mode_auto=true and only the snippets that fired on that request are flipped to paused. |
| Recovery URL | If the admin is locked out, call OCI_Settings::get_recovery_url() from a must-use plugin or WP-CLI shell. It returns a URL containing a one-time, 24h-expiry token that disables Safe Mode when visited. |
https://example.com/my-page/?oci_safe=1
# admin bar reads: ⚠ OCI Safe Mode (auto) when triggered by the shutdown handler
Diagnosing issues
?oci_safe=1 (logged in as admin) to confirm the issue is snippet-related.published.Evaluation order
All conditions must pass for a snippet to fire:
- 1Is the plugin globally enabled (
enabled)? - 2Is Safe Mode off (
safe_mode)? - 3If
?oci_safe=1is set, does the user havemanage_options? (If yes, snippets are bypassed.) - 4Snippet status is
published? - 5Schedule (start / end) currently in range?
- 6Page type matches (or page_types contains
all)? - 7User condition matches (logged-in / logged-out)?
- 8URL pattern matches (or no patterns set)?
- 9Device matches (mobile / desktop / all)?
- 10User role matches (or set to
all)? - 11If
type='php':oci_is_pro()returns true?
CodeMirror editor
OCodeInsert uses WordPress's built-in CodeMirror editor (wp-codemirror), with no external CDN or library dependencies. Loaded only on the Snippets page.
- Syntax highlighting for HTML, JavaScript, CSS, and PHP
- Line numbers
- Bracket and tag matching
- Auto-indent
- Mode switches automatically when you change the Type dropdown
| Type | CodeMirror mode |
|---|---|
| HTML | text/html (HTML mixed mode, supports embedded JS and CSS) |
| JavaScript | text/javascript |
| CSS | text/css |
| PHP | application/x-httpd-php |
Data storage & uninstall
v2.0.0 introduced a custom database schema. Snippets, revision history, and execution logs each live in their own table; lightweight settings remain in wp_options.
Tables
| Table | Columns | Limits |
|---|---|---|
oci_snippets | id (uuid v4), title, type, code (LONGTEXT), location, status, priority, conditions (JSON), created_at, updated_at. Indexed on (status, location) and priority. | No row cap. Code field hard-capped at 512 KB on import. |
oci_snippet_revisions | revision_id (auto-inc), snippet_id, code, conditions, status, saved_by (user_id), saved_at, label. Indexed on snippet_id and saved_at. | 25 revisions per snippet. Older rows pruned on each save. |
oci_execution_log | log_id (auto-inc), snippet_id, url (max 500), user_id, fired_at. Indexed on snippet_id and fired_at. | 10,000 rows total. Oldest pruned on insert. |
Options stored
| Option key | Contents |
|---|---|
oci_settings | Array: enabled, safe_mode, safe_mode_auto, safe_mode_reason, allowed_roles, recovery_token (hashed), recovery_expires |
oci_db_version | Schema version string. Compared on plugins_loaded to trigger OCI_DB::install(). |
oci_code_snippets_v1_backup | Raw v1 options-blob payload preserved after OCI_DB::maybe_migrate_from_options() runs. Safe to delete after verifying v2 data. |
oci_error_log | Array of recent runtime error entries from OCI_Settings::log_error(). Capped at 500 entries. |
Snippet schema (hydrated)
JSON{
"id": "uuid-v4",
"title": "string",
"type": "html | js | css | php",
"code": "string (raw)",
"location": "head | footer | before_content | after_content | after_paragraph | before_comments | after_comments | login_page | 404_page | search_results | admin_head | orravo_*",
"status": "published | draft | paused | archived",
"priority": 10,
"page_types": ["all" | "front" | "single" | "archive" | "search" | "404"],
"pages": "comma-separated URL patterns; * is a wildcard",
"user_condition": "all | logged_in | logged_out",
"user_role": "all | ",
"device": "all | mobile | desktop",
"schedule_start": "Y-m-d H:i:s or empty",
"schedule_end": "Y-m-d H:i:s or empty",
"paragraph_num": 1,
"created": "Y-m-d H:i:s",
"updated": "Y-m-d H:i:s"
}
conditions column, then hydrated back into the array above by OCI_Snippets::hydrate().Migration from v1
v1 stored snippets in a single JSON-encoded option (oci_code_snippets). On first load of v2, OCI_DB::maybe_migrate_from_options() walks that array, inserts each snippet into the new oci_snippets table with default targeting, copies the original payload to oci_code_snippets_v1_backup, and deletes oci_code_snippets. The backup option is kept so admins can verify the migration before manually deleting it.
Uninstall
Deleting via Plugins → Delete runs uninstall.php, which calls OCI_DB::drop_tables() to drop all 3 custom tables and removes the related options. Deactivation alone leaves the data in place.
What ships free and what needs Pro
v2.0.0 ships every targeting rule, the OIntel linter, the snippet library, revisions, Safe Mode auto-recovery, and the recovery URL token in the free build. The Pro license unlocks one capability: server-side PHP snippet execution.
Free in v2.0.0
oci_snippet_revisions. Restore any revision via OCI_Snippets::restore_revision().?oci_safe=1 bypass for admins, shutdown handler that auto-pauses fired snippets on PHP fatal, and one-time recovery URL token.OCI_Linter::lint() scans for obfuscation patterns, dangerous PHP functions, render-blocking scripts, and broad-targeting hints.libraries/snippets.json, with {{PLACEHOLDER}} token substitution via OCI_Library::apply_placeholders().oci_execution_log records every snippet fire with URL and user_id. Capped at 10,000 rows, auto-pruned.[ocodeinsert id="..."] renders any published snippet inside post content.Pro license unlocks
php snippet type is double-gated: saving requires the WordPress unfiltered_html capability, and runtime execution additionally requires oci_is_pro() to return true. Code runs inside ob_start() with a wrapped error handler so failures land in the error log instead of the page output.orravo_product_licensed() / orravo_is_licensed()) or the OCI_PRO constant for self-hosted licensing.Developer reference
All PHP methods are static and available after init.
PHP API: OCI_Snippets
PHP// Read
$snippets = OCI_Snippets::get_all( $include_archived = false );
$snippet = OCI_Snippets::get_by_id( 'uuid' );
// Save (create when 'id' is empty, otherwise update + auto-revision)
$id = OCI_Snippets::save_snippet([
'id' => '', // empty = create new (uuid v4 generated)
'title' => 'My Script',
'type' => 'js', // html | js | css | php
'code' => 'alert(1);',
'location' => 'footer', // any of LOCATIONS or THEME_LOCATIONS
'status' => 'published', // published | draft | paused | archived
'priority' => 10,
'page_types' => ['all'],
'pages' => '', // comma-separated patterns; * is wildcard
'user_condition' => 'all', // all | logged_in | logged_out
'user_role' => 'all',
'device' => 'all', // all | mobile | desktop
'schedule_start' => '',
'schedule_end' => '',
'paragraph_num' => 1,
]);
// Lifecycle
OCI_Snippets::set_status( 'uuid', 'paused' );
$new_status = OCI_Snippets::toggle_snippet( 'uuid' ); // 'published' <-> 'paused'
OCI_Snippets::disable_snippet( 'uuid' ); // shortcut for set_status('paused')
$copy_id = OCI_Snippets::duplicate_snippet( 'uuid' ); // creates draft '... (Copy)'
OCI_Snippets::delete_snippet( 'uuid' ); // also deletes revisions + log rows
// Revisions
$ok = OCI_Snippets::restore_revision( $revision_id ); // saves current as new revision then loads
$rows = OCI_DB::get_revisions( 'uuid' ); // up to 25, newest first
// Render helpers
$html = OCI_Snippets::prepare_code_public( $snippet );
PHP API: OCI_Settings, OCI_DB, OCI_Linter, OCI_Library
PHP// Settings
$settings = OCI_Settings::get_all();
$enabled = OCI_Settings::is_enabled();
$safe_mode = OCI_Settings::is_safe_mode();
$can_access = OCI_Settings::can_access();
OCI_Settings::enable_safe_mode( 'manual' ); // or 'auto'
OCI_Settings::disable_safe_mode();
OCI_Settings::log_error( 'uuid', 'message' );
$log = OCI_Settings::get_error_log(); // capped at 500 entries
// Recovery URL token (24h, single-use)
$url = OCI_Settings::get_recovery_url();
// Execution log (oci_execution_log)
$last = OCI_DB::get_last_fired( 'uuid' );
$rows = OCI_DB::get_execution_log( 'uuid', 20 );
$has = OCI_DB::snippet_has_errors( 'uuid' );
// Linter
$report = OCI_Linter::lint( $snippet ); // ['errors' => [...], 'warnings' => [...]]
// Library (built-in templates with placeholders)
$cats = OCI_Library::get_categories();
$saveable = OCI_Library::prepare_for_save( $library_id, [ 'GA_ID' => 'G-XXX' ] );
$id = OCI_Snippets::save_snippet( $saveable );
// Pro entitlement check
if ( oci_is_pro() ) { /* PHP snippets allowed */ }
Shortcode
HTML[ocodeinsert id="snippet-uuid"]
Renders the snippet inline inside post content. Skips snippets that are not published.
Constants
| Constant | Default | Purpose |
|---|---|---|
OCI_VERSION | '2.0.0' | Plugin version string |
OCI_DB_VERSION | '2.0.0' | Schema version. Change forces OCI_DB::install() on plugins_loaded. |
OCI_DIR | plugin_dir_path(__FILE__) | Absolute path to plugin directory |
OCI_URL | plugin_dir_url(__FILE__) | URL to plugin directory |
OCI_FILE | __FILE__ | Used by activation hooks |
OCI_PRO | undefined | Define as true to opt into PHP execution without the Orravo licensing helpers |
PHP// Enable PHP snippet execution in a custom build (wp-config.php or must-use plugin)
define( 'OCI_PRO', true );
Filters
PHP// Register OCodeInsert with the Orravo Mini-Core (already wired by the plugin)
add_filter( 'orravo_register_product', function( array $p ): array {
return array_merge( $p, [ 'ocodeinsert' => 'OCodeInsert' ] );
} );
add_filter( 'orravo_mini_admin_pages', function( array $p ): array {
return array_merge( $p, [ 'ocodeinsert' ] );
} );
status='paused' or use the targeting fields (URL pattern, schedule, role) which already short-circuit at the location query.What's changed
- NewCustom database schema:
oci_snippets,oci_snippet_revisions,oci_execution_log - NewAutomatic migration from v1 options-blob into the new schema; legacy data preserved as
oci_code_snippets_v1_backup - NewFourth snippet type:
php, double-gated byunfiltered_html+oci_is_pro(), executed viaob_start()+eval()with a wrapped error handler - New11 injection locations (added after_paragraph, before/after_comments, login_page, 404_page, search_results, admin_head)
- New7 OTheme-specific hook locations active when the Orravo theme is detected
- NewTargeting expanded with
user_role,device(mobile/desktop), andschedule_start/schedule_end - New4-state status model:
published,draft,paused,archived - NewRevision history capped at 25 per snippet, restorable via
OCI_Snippets::restore_revision() - NewPer-snippet execution log capped at 10,000 rows, auto-pruned on insert
- NewOIntel linter (
OCI_Linter): obfuscation, dangerous PHP funcs, document.write, render-blocking, broad-targeting hints - NewSnippet library (
OCI_Library): JSON-defined templates with{{PLACEHOLDER}}substitution - NewSafe Mode auto-recovery via
register_shutdown_function: enables Safe Mode and pauses only fired snippets when a PHP fatal is detected - NewRecovery URL token (24h, single-use) via
OCI_Settings::get_recovery_url() - NewAdmin bar status node: green dot when healthy, amber when Safe Mode is active
- New
?oci_safe=1URL bypass scoped to admins (manage_options) only - NewShortcode
[ocodeinsert id="..."]for inline rendering inside post content - NewOrravo Mini-Core integration: registers OCodeInsert as a managed product with shared admin pages
- New
uninstall.phpdrops all 3 custom tables and removes plugin options
- NewSnippet storage in WP options
- New3 injection locations (head, before content, after content)
- NewPriority system
- NewURL pattern targeting
- NewActive/inactive toggle
- NewAJAX admin (save, delete, toggle)
Frequently asked questions
oci_snippets table (and a revisions table for history). Updating or reinstalling the plugin does not delete snippet data; only deleting the plugin triggers the full uninstall cleanup.published, schedule in range, page type matches, user condition matches, URL pattern matches, device matches, role matches. For php snippets the additional check is oci_is_pro(). If using before_content or after_content, ensure the theme runs the_content; if not, the JS fallback in wp_footer handles insertion.unfiltered_html capability (administrators only by default). At execution time, oci_is_pro() must also return true, so the PHP type effectively requires the Pro license. Code runs inside ob_start() with a wrapped error handler so syntax errors and exceptions are captured into the error log.head and footer locations always work. For before_content / after_content, if the page builder bypasses the_content filter, OCodeInsert's JS fallback handles injection. For most page builders, use head or footer for maximum reliability.oci_snippets), so storing thousands has no per-page-load penalty.?oci_safe=1 to any URL to bypass for that request only (the check requires manage_options). (3) If a fatal already triggered the shutdown handler, Safe Mode (auto) is on and only the snippets that fired on the failing request are paused; flip those snippets back to published after editing.oci_snippets table: one row per snippet with title, type, code, location, status, priority, conditions (JSON blob), and timestamps. Revision history lives in oci_snippet_revisions and execution log lives in oci_execution_log. Edit through the admin UI or the static OCI_Snippets PHP API; v2.0.0 does not expose REST endpoints.Got a question about OCodeInsert?
Reach out directly. Kenneth replies within 24 hours.

