v1.0.0
MySQL / SQLite PHP 8.1+
DraftVertex CMS
Lightweight, modular PHP CMS with a WordPress-inspired hooks & filters architecture
PHP required
8.1+
Database
MySQL / SQLite
Architecture
Hook-driven

System attributes

AttributeValue
Version1.0.0
DB version1
DB prefixdv_
Front controllerindex.php → bootstrap.php → router.php
Template systemLayout-based with fallback hierarchy
Admin slug/dv-admin
API base/dv-api
Function prefixdv_

Boot order

index.php
└── dv-includes/bootstrap.php
    ├── hooks.php        # action/filter engine
    ├── db.php           # PDO wrapper
    ├── options.php      # key/value site settings
    ├── session.php      # secure sessions + CSRF
    ├── permissions.php  # RBAC
    ├── enqueue.php      # scripts/styles
    ├── extensions       # load active extensions
    ├── layouts          # load active layout functions.php
    └── router.php → template
File structure
Complete directory layout of a DraftVertex installation

Root

dv-cms/
├── .htaccess              # Rewrite rules (managed via Permalink settings)
├── index.php              # Front-end entry point
├── ajax.php               # AJAX handler entry point
├── dv-config.php          # Database, keys, paths, debug settings
├── dv-config-sample.php   # Sample config (copy to dv-config.php)
├── sitemap.xsl
└── src/
    ├── dv-admin/          # Admin dashboard pages
    ├── dv-includes/       # Core engine files
    ├── dv-setup/          # Installation wizard
    └── dv-content/        # User-generated content
        ├── cache/
        ├── uploads/
        ├── layouts/
        │   ├── layout-101/
        │   ├── modern-alpha/
        │   └── draftvertex-classic/
        └── extensions/
            └── seo-engine/

dv-includes/ (engines)

bootstrap.php
hooks.php
db.php
options.php
session.php
permissions.php
router.php
enqueue.php
rest-api.php
post-types.php
taxonomies.php
cache.php
cron-engine.php
mail-engine.php
media-engine.php
comments-engine.php
canvas-builder.php
visual-engine.php
layout-engine.php
extension-engine.php
menus-engine.php
widgets-engine.php
shortcodes.php
site-health-engine.php
sitemaps.php
template-tags.php
ajax-handler.php
recovery.php
class-dv-sanitizer.php
class-dv-list-table.php
mailer/class-dv-smtp.php

dv-admin/ (pages)

index.php
auth.php
posts.php
media.php
users.php
categories.php
comments.php
menus.php
components.php
visuals.php
preferences.php
profile.php
extensions.php
extensions-editor.php
extensions-install.php
layouts.php
layouts-editor.php
layouts-install.php
tools.php
upgrade.php
menu-registry.php
partials/
assets/
Configuration
Constants defined in dv-config.php — copy from dv-config-sample.php
All constants have safe defaults in their engine files. Only define what you need to override.

Database

ConstantDefaultDescription
DV_DB_DRIVERmysqlmysql or sqlite
DV_DB_HOSTlocalhostDatabase hostname
DV_DB_NAMEdatabase_nameDatabase name or SQLite filename
DV_DB_USERusernameDatabase username
DV_DB_PASSWORDpasswordDatabase password
DV_DB_CHARSETutf8mb4Connection charset
DV_DB_PREFIXdv_Table prefix (configurable)

Security keys & salts

ConstantDescription
DV_AUTH_KEYAuthentication key (random hex)
DV_SECURE_AUTH_KEYSecure auth key
DV_LOGGED_IN_KEYLogged-in cookie key
DV_NONCE_KEYNonce hashing key
DV_AUTH_SALTAuth salt
DV_NONCE_SALTNonce salt

Paths & URLs

ConstantDescription
DV_SITE_URLPublic site URL (e.g. http://localhost:8000)
DV_ADMIN_URLAdmin URL (SITE_URL + /dv-admin)
DV_ROOT_PATHAbsolute filesystem root (auto-detected)
DV_SRC_PATHPath to src/
DV_INCLUDES_PATHPath to dv-includes/
DV_ADMIN_PATHPath to dv-admin/
DV_CONTENT_PATHPath to dv-content/
DV_UPLOADS_PATHPath to uploads/
DV_CONTENT_URLContent URL
DV_UPLOADS_URLUploads URL

Debug, cache & limits

ConstantDefaultDescription
DV_DEBUGfalseEnable error reporting
DV_DEBUG_LOGfalseLog errors to debug.log
DV_DEBUG_DISPLAYfalseDisplay errors on screen
DV_CACHEfalseEnable page caching
DV_CACHE_TTL3600Cache lifetime in seconds
DV_MAX_UPLOAD_SIZE64MMax upload file size
DV_ALLOWED_EXTENSIONSjpg,jpeg,png…Allowed file types
DV_DISABLE_CRONfalseDisable built-in cron runner
DV_SESSION_NAMEdv_sessionSession cookie name
DV_SESSION_LIFETIME86400Session duration in seconds
DV_LOGIN_LOCKOUT_ATTEMPTS5Lockout threshold
DV_LOGIN_LOCKOUT_DURATION900Lockout duration in seconds
DV_ADMIN_SLUGdv-adminAdmin URL slug
Installation wizard
5-step guided setup at dv-setup/install.php

Setup steps

StepPurpose
1 — WelcomeRequirements check: PHP 8.1+, PDO extension, mbstring, writable directories
2 — DatabaseConfigure MySQL or SQLite connection and test it
3 — Site infoSite name, public URL, admin email
4 — Admin accountCreate administrator username and password
5 — CompleteSuccess page with links to admin panel and front-end site

What the installer does

ActionDetail
Runs SQL schemaschema.sql for MySQL, schema_sqlite.sql for SQLite
Creates admin userUPSERT — preserves existing account data
Sets site optionsname, URL, admin email stored in dv_options
Generates dv-config.phpWith cryptographically random salts and keys
Creates uploads dirWith index.php protection file
Security: Remove or restrict the dv-setup/ directory after installation completes.
Hooks system
WordPress-compatible action & filter engine — dv-includes/hooks.php

Core functions

FunctionPurpose
dv_add_action($hook, $cb, $priority, $args)Register a callback on an action hook
dv_do_action($hook, ...$args)Fire all callbacks on an action hook
dv_has_action($hook)Check if a hook has registered callbacks
dv_remove_action($hook, $cb, $priority)Remove a callback from an action
dv_add_filter($hook, $cb, $priority, $args)Register a filter callback
dv_apply_filters($hook, $value, ...$args)Apply filter callbacks to a value and return result
Actions run code at specific moments. Filters modify data before it is used. Lower priority = earlier execution (default is 10).

Usage examples

// Action: run code when a post is saved
dv_add_action('dv_save_post', function($post_id, $post_data) {
    error_log("Post {$post_id} was saved");
}, 10, 2);

// Filter: append a footer note to all content
dv_add_filter('dv_the_content', function($content) {
    return $content . '<p>— Footer note</p>';
});

// Filter: restrict upload size to 2 MB
dv_add_filter('dv_max_upload_size', fn() => 2 * 1024 * 1024);

// Action: add a custom field to the registration form
dv_add_action('dv_register_form_fields', function() {
    echo '<div class="dv-auth-form-row">';
    echo '  <label>Twitter Handle</label>';
    echo '  <input type="text" name="twitter">';
    echo '</div>';
});
Actions reference
48 built-in action hooks fired across the system — use dv_add_action() to hook in
Hook nameFileArguments
dv_loadedbootstrap.php
dv_initbootstrap.php
dv_auth_initauth.php$action
dv_before_auth_verifyauth.php$username, $password
dv_user_loginauth.php$user_id, $user
dv_user_registeredauth.php$user_id
dv_user_registered_unverifiedauth.php$user_id
dv_user_email_verifiedauth.php$user_id
dv_send_email_verification_emailauth.php$user, $token
dv_send_password_reset_emailauth.php$user, $token
dv_auth_card_topauth.php$action
dv_auth_form_beforeauth.php$action
dv_login_form_topauth.php
dv_login_form_fieldsauth.php
dv_register_form_topauth.php
dv_register_form_fieldsauth.php
dv_forget_password_form_fieldsauth.php
dv_reset_password_form_fieldsauth.php
dv_auth_social_buttonsauth.php$action
dv_auth_card_bottomauth.php$action
dv_auth_custom_action_{$action}auth.php
dv_save_postposts.php$post_id, $_POST
dv_post_updatedposts.php$post_id, $data
dv_edit_post_form_mainposts.php$post
dv_edit_post_form_sidebarposts.php$post
dv_media_uploadedmedia-engine.php$file_id, $file_data
dv_media_deletedmedia-engine.php$file_id
dv_media_edit_before_detailsmedia.php$file
dv_media_edit_after_detailsmedia.php$file
dv_comment_postcomments-engine.php$comment_id
dv_deleted_commentcomments-engine.php$comment_id
dv_edited_commentcomments-engine.php$comment_id
dv_user_savedusers.php$user_id, $_POST
dv_user_edit_formusers.php$user
dv_profile_savedprofile.php$user_id, $_POST
dv_profile_formprofile.php$user
dv_edited_termcategories.php$term_id, $taxonomy
dv_created_termcategories.php$term_id, $taxonomy
dv_deleted_termcategories.php$term_id, $taxonomy
dv_template_redirectrouter.php$resolved
dv_register_customizervisual-engine.php
dv_customizer_save_settingvisuals.php$setting_id, $value
dv_customizer_publishedvisuals.php$settings
dv_extension_activatedextension-engine.php$slug
dv_extension_deactivatedextension-engine.php$slug
dv_rest_api_initrest-api.php
dv_admin_headdv-header.php
dv_admin_footerdv-footer.php
dv_headtemplate-tags.php
dv_footertemplate-tags.php
dv_enqueue_scriptsenqueue.php
dv_option_updatedoptions.php$key, $value
dv_registered_post_typepost-types.php$post_type
dv_registered_taxonomytaxonomies.php$taxonomy
dv_get_site_healthsite-health-engine.php$results
dv_htaccess_updatedbootstrap.php$path, $structure
Filters reference
36 built-in filter hooks — use dv_add_filter() to modify values
Filter nameReturnsDescription
dv_allow_authboolAllow auth page access
dv_auth_allowed_actionsarrayAllowed auth action list
dv_auth_user_lookuparray|nullOverride user lookup result
dv_login_redirectstringPost-login redirect URL
dv_allow_registrationboolAllow new user registration
dv_registration_errorsarrayRegistration validation errors
dv_default_user_rolestringDefault role for new users
dv_email_verification_requiredboolRequire email verification on register
dv_auth_noticesarrayCustom auth page notices
dv_login_headerurlstringAuth page logo URL
dv_login_body_classstringAuth page body CSS class
dv_login_stylesheetstringAuth page custom CSS URL
dv_max_upload_sizeintMax upload size in bytes
dv_allowed_mime_typesarrayAllowed MIME types map
dv_allowed_extensionsarrayAllowed file extensions
dv_pre_handle_media_uploadboolPre-upload validation gate
dv_media_upload_argsarrayUpload handling arguments
dv_media_query_argsarrayMedia library query args
dv_media_modal_htmlstringFull media modal HTML
dv_get_avatarstringUser avatar HTML
dv_mail_argsarrayEmail args (to, subject, message)
pre_dv_mailmixedPre-send mail handler / short-circuit
dv_smtp_configarraySMTP configuration array
dv_get_commentsarrayRetrieved comments list
dv_preprocess_commentarrayComment data before insert
dv_comment_is_spamboolSpam detection result
dv_comment_form_argsarrayComment form configuration
dv_customizer_sectionsarrayCustomizer sections list
dv_customizer_controlsarrayCustomizer controls list
dv_registered_canvas_blocksarrayRegistered canvas blocks
dv_user_capabilitiesarrayUser extra capabilities
dv_page_templatesarrayAvailable page templates
dv_templatestringTemplate file path to load
dv_the_titlestringPost title output
dv_the_contentstringPost content output
dv_body_classarrayBody CSS class array
dv_nav_menustringRendered navigation menu HTML
dv_registered_widgetsarrayRegistered widgets
dv_htaccess_managed_contentstringRules inside <IfModule> block
dv_htaccess_contentstringFull .htaccess content
dv_dashboard_widgetsarrayAdmin dashboard widget list
Router & permalinks
URL resolution engine — dv-includes/router.php

Permalink modes

ModeURL formatDescription
plain/?p=123Query string only, no rewriting required
postname/hello-worldClean SEO-friendly slugs (recommended)
custom/2026/06/hello-worldCustom structure with date/name tags

Custom structure tags

%year%%monthnum%%day% %postname%%post_id% %category%%author%

Template resolution order (single post)

#Path checked (first match wins)
1layouts/{active}/templates/single-{post-type}.php
2layouts/{active}/templates/single.php
3layouts/{active}/single.php
4layouts/{active}/templates/archive.php
5layouts/{active}/templates/index.php
6layouts/{active}/index.php
7Built-in fallback HTML layout
Database schema
MySQL 5.7+ or SQLite 3.x — all tables use the dv_ prefix
Foreign keys use CASCADE deletes. dv_metadata is a unified store — distinguish objects via object_type: user / post / term / comment / media.
TableKey columnsPurpose
dv_optionsid, option_key, option_value, autoloadSite-wide key/value settings
dv_metadataid, object_type, object_id, meta_key, meta_valueUnified meta store for any object
dv_usersid, username (UNIQUE), password_hash, email (UNIQUE), display_name, role, statusUser accounts
dv_postsid, author_id (FK), post_parent, title, slug (UNIQUE per type), content_raw, canvas_layout_json, post_type, statusPosts, pages, custom post types
dv_categoriesid, name, slug, taxonomy, parent, description, countTaxonomy terms (categories, tags…)
dv_term_relationshipsid, post_id (FK), term_id (FK)Post ↔ term associations
dv_commentsid, post_id (FK), user_id, guest_name, guest_email, content, statusPost comments
dv_mediaid, filename, filepath, mime_type, filesize, uploaded_by (FK), alt_text, captionMedia file library
dv_sessionsid, session_id, user_id (FK), ip_address, user_agent, last_activeDatabase-backed sessions
dv_login_attemptsid, ip_address, attempted_atLockout tracking
dv_nav_menusid, location, items (JSON)Navigation menus
dv_widget_assignmentsid, sidebar_id, widget_type, widget_args (JSON), sort_orderSidebar widget assignments
Caching system
Full page output cache + transient object cache — dv-includes/cache.php

Transient API

FunctionDescription
dv_set_transient($key, $value, $expiration)Store data with optional TTL
dv_get_transient($key)Retrieve cached data (false if expired)
dv_delete_transient($key)Delete a single cached item
dv_clear_page_cache()Wipe all cached page HTML files
Page cache is auto-cleared on post create/update via dv_post_created. Enable with DV_CACHE = true and tune TTL with DV_CACHE_TTL.

Page cache scope

Logged-out visitors onlyAuthenticated requests bypass the cache
Cache files locationdv-content/cache/
Auto-invalidated onPost save, post update, option change
Cron scheduler
Scheduled background events — dv-includes/cron-engine.php

API methods

MethodDescription
DV_Cron::scheduleEvent($timestamp, $recurrence, $hook, $args)Schedule a recurring event
DV_Cron::unscheduleEvent($hook)Remove a scheduled event
DV_Cron::runDue()Execute all currently-due events
DV_Cron::checkAndRun()Check and trigger due events (called on each page load)

Recurrence intervals

KeySecondsDuration
hourly36001 hour
daily8640024 hours
weekly6048007 days
Disable the built-in cron runner with DV_DISABLE_CRON = true and use a real server cron job instead for better reliability.
Session & CSRF protection
Secure session handling with CSRF nonce tokens — dv-includes/session.php

CSRF nonce functions

FunctionDescription
dv_create_nonce($action)Generate a time-limited CSRF nonce token
dv_verify_nonce($token, $action)Verify a nonce token — returns bool
dv_nonce_field($action)Output a hidden nonce <input> field
dv_check_nonce($action)Verify nonce from POST/GET — dies on failure

Session methods

MethodDescription
DV_Session::start()Initialize secure session with hardened cookie settings
DV_Session::login($user_id, $user)Authenticate user, regenerate session ID
DV_Session::logout()Log out and destroy current session
DV_Session::destroy()Fully destroy session and cookies

Cookie security settings

HttpOnlyCookies not accessible via JavaScript
SameSiteLax — prevents CSRF from cross-site links
SecureEnforced automatically when HTTPS detected
Session fixationID regenerated on every login
Media engine
File uploads, validation, and media library — dv-includes/media-engine.php

Key functions

FunctionDescription
dv_handle_media_upload(array $file)Process an uploaded file and add to media library
dv_get_media(int $id)Get a single media item by ID
dv_get_all_media(array $args)Query the media library with filters
dv_delete_media(int $id)Delete media item and its file on disk
dv_get_media_url(int $id)Get public URL of a media item
dv_media_input($name, $value, $args)Render a media picker input field
dv_get_avatar(int $user_id, array $args)Get user avatar HTML

Relevant filters

dv_max_upload_sizeOverride max upload bytes
dv_allowed_mime_typesAllowed MIME types array
dv_allowed_extensionsAllowed file extension list
dv_pre_handle_media_uploadShort-circuit validation (return false to block)
dv_media_modal_htmlCustomise media picker modal HTML
Comments engine
Threaded comments with spam filtering and moderation — dv-includes/comments-engine.php

Key functions

FunctionDescription
dv_get_comments(int $post_id, array $args)Get comments for a post
dv_insert_comment(array $data)Insert a new comment — returns comment ID
dv_delete_comment(int $comment_id)Delete a comment permanently
dv_update_comment(int $comment_id, array $data)Update comment content or status
dv_comment_form(array $args)Render the comment submission form

Relevant filters

dv_preprocess_commentModify comment data before insert
dv_comment_is_spamReturn true to mark as spam
dv_comment_send_notificationControl notification email sending
dv_comment_form_argsCustomise comment form configuration
Canvas builder
Drag-and-drop block page builder — dv-includes/canvas-builder.php

Built-in blocks

Block typeDefault data fields
paragraphcontent, size, align, color, link, bold, italic
headingcontent, level (h1–h6), align, color
imageurl, caption, alt, align, width, radius
buttontext, link, align, shape, bg_color, text_color
quotecontent, citation, color
videocontent (iframe or URL)
htmlcontent, enable_style, enable_script
dividerstyle, height, color, margin

Registering a custom block

dv_register_canvas_block('my_block', [
    'name'         => 'My Block',
    'icon'         => 'icon-name',
    'description'  => 'What this block does',
    'default_data' => [
        'field1' => 'value1',
        'field2' => 'value2',
    ]
]);

Use the dv_registered_canvas_blocks filter to programmatically add or remove blocks from the list.

Visual customizer
Live preview theme settings engine — dv-includes/visual-engine.php

Control types

TypeDescription
textSingle-line text input
textareaMulti-line text area
selectDropdown (requires choices array)
checkboxBoolean on/off toggle
radioRadio button group
imageMedia upload selector
select_pagePage dropdown (from dv_posts)
layout_selectorInstalled layout picker
htmlStatic HTML display (info/divider)

Registering sections & controls

dv_add_action('dv_register_customizer', function() {

    DV_Visual_Customizer::register_section('my_section', [
        'title'    => 'My Section',
        'priority' => 10,
    ]);

    DV_Visual_Customizer::register_control('my_setting', [
        'section'     => 'my_section',
        'type'        => 'text',
        'label'       => 'My Setting',
        'description' => 'Help text for this control',
        'default'     => 'default value',
        'priority'    => 10,
    ]);
});

// Read the value anywhere
$value = dv_get_option('my_setting', 'default');
Extension system
Plugins live in dv-content/extensions/{slug}/ — managed via Admin → Extensions

File structure

dv-content/extensions/my-extension/
├── my-extension.php    # Main file (required — must match folder name)
├── assets/             # CSS, JS, images
├── templates/          # Optional template files
└── README.md

Main file header & example

<?php
/**
 * Extension Name: My Extension
 * Version:        1.0.0
 * Description:    What this extension does
 * Author:         Your Name
 */

dv_add_action('dv_init', function() {
    // Runs when CMS is fully initialised
});

dv_add_filter('dv_the_content', function($content) {
    return $content . '<p>Added by My Extension</p>';
});

Extension engine API

MethodDescription
DV_ExtensionEngine::getInstalled()Scan and return all installed extension metadata
DV_ExtensionEngine::getActive()Get array of active extension slugs
DV_ExtensionEngine::loadActive()Require and execute all active extensions
DV_ExtensionEngine::activate(string $slug)Activate an extension by slug
DV_ExtensionEngine::deactivate(string $slug)Deactivate an extension by slug
Extensions have full access to all action and filter hooks. Use dv_apply_filters() in your own extension to make it extensible by others.
Layout system
Themes live in dv-content/layouts/{slug}/ — managed via Admin → Layouts

File structure

dv-content/layouts/my-layout/
├── my-layout.php       # Main file with header (required)
├── functions.php       # Auto-included on activation
├── style.css           # Auto-enqueued on frontend
├── templates/
│   ├── index.php
│   ├── single.php
│   ├── page.php
│   ├── archive.php
│   └── full-width.php  # Custom page template
└── assets/

Layout header + custom template

<?php
/**
 * Layout Name: My Layout
 * Version:     1.0.0
 * Description: A beautiful layout for DraftVertex CMS
 * Author:      Your Name
 */
// Custom page template (templates/full-width.php)
<?php
/**
 * Template Name: Full Width Page
 */
// Your template code here…

Template tags available in layouts

FunctionDescription
dv_the_title()Display current post title
dv_the_content()Display current post content
dv_the_excerpt()Display post excerpt
dv_the_permalink()Display post URL
dv_the_author()Display post author name
dv_the_date()Display post date
dv_the_thumbnail()Display featured image
dv_has_posts()Check if posts exist in the current loop
dv_the_post()Advance to next post in loop
dv_body_class()Output body CSS class string
dv_head()Output <head> section hooks
dv_footer()Output footer hooks
dv_get_nav_menu($location)Get rendered navigation HTML for a location
dv_get_avatar($user_id)Get user avatar HTML
dv_get_comments($post_id)Get comments array for a post
dv_get_categories($post_id)Get category terms for a post
dv_is_logged_in()Check if current user is authenticated
REST API
JSON endpoints at /dv-api/{route}dv-includes/rest-api.php

Built-in endpoints

MethodRouteDescription
GET/dv-api/postsGet all published posts
GET/dv-api/posts/{id}Get single post by ID
GET/dv-api/pagesGet all published pages
GET/dv-api/optionsSite name, tagline, and public URL

Registering custom routes

dv_add_action('dv_rest_api_init', function() {

    DV_REST_API::registerRoute(
        'GET',
        'my-endpoint',
        function() {
            return ['status' => 'ok', 'data' => 'Hello from API'];
        }
    );

    // POST route with auth check
    DV_REST_API::registerRoute(
        'POST',
        'my-endpoint',
        function() {
            dv_require_cap('edit_posts');
            $data = json_decode(file_get_contents('php://input'), true);
            return ['received' => $data];
        }
    );
});
Mail engine & SMTP
Email dispatch with optional SMTP — dv-includes/mail-engine.php

Core functions

FunctionDescription
dv_mail($to, $subject, $message, $headers)Send an email — returns bool on success
dv_get_mail_config()Get current mail / SMTP configuration

SMTP configuration via filter

dv_add_filter('dv_smtp_config', function($config) {
    return [
        'host'      => 'smtp.gmail.com',
        'port'      => 587,
        'secure'    => 'tls',      // 'tls' or 'ssl'
        'username'  => 'user@gmail.com',
        'password'  => 'app-password',
        'from'      => 'noreply@example.com',
        'from_name' => 'My Site',
    ];
});
Use the pre_dv_mail filter to short-circuit email sending (useful for logging or queueing in testing environments).
User system & permissions
Role-based access control — dv-includes/permissions.php

Built-in roles

RoleCapabilities
administratorFull system access — manage options, users, posts, media, layouts, extensions
editorPublish and manage all posts/pages, moderate comments, manage categories
authorPublish and manage own posts, upload media
contributorWrite own posts (cannot publish), no media upload
subscriberRead-only, can manage own profile

Key functions

FunctionDescription
dv_current_user()Get current logged-in user data array
dv_current_user_id()Get current user ID (0 if guest)
dv_current_user_role()Get current user role string
dv_is_logged_in()Check if a user is authenticated
dv_require_login()Redirect to login page if not authenticated
dv_require_cap($cap)Redirect with 403 if user lacks capability
dv_user_can($user_id, $cap)Check if a specific user has a capability
dv_get_user_meta($user_id, $key, $default)Get user metadata value
dv_update_user_meta($user_id, $key, $value)Set or update user metadata
Security guidelines
Hardening your DraftVertex CMS installation
Always review and apply these guidelines before deploying to a production server.

File permissions

PathPermissionReason
dv-config.php600 or 640Contains database credentials and salts
dv-content/755 dirs / 644 filesWeb-accessible user content
.htaccess644Web server must read it

Built-in .htaccess protections

Block dv-includes/Returns 403 Forbidden on direct access
Block dv-setup/Returns 403 Forbidden on direct access
Block dotfilesHidden files (.env, .git, etc.) blocked
Block sensitive extensions.sql, .log, .env files return 403
Disable directory listingOptions -Indexes applied globally
Hide PHP versionHeader X-Powered-By suppressed
Block TRACE / TRACKDangerous HTTP methods rejected
Block vendor/node_modulesDevelopment directories blocked

Production checklist

Always use HTTPSUse a valid TLS certificate in production
Set DV_DEBUG = falseNever expose error details publicly
Remove dv-setup/After installation is complete
Unique salts and keysAuto-generated during wizard installation
Regular backupsDatabase + dv-content/uploads/
Strong admin passwordUse a password manager
.htaccess reference
Auto-generated by dv_generate_htaccess() — configured via Preferences → Permalinks

Marker system

# BEGIN DV CMS  ← Start of managed block (auto-generated)
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    ...
</IfModule>
# END DV CMS    ← End of managed block

# Developer custom rules go OUTSIDE the markers
# They are preserved on every regeneration

Generated rewrite rules

PurposePattern → Target
Admin assets^dv-admin/assets/(.*) → src/dv-admin/assets/$1
Content files^dv-content/(.*) → src/dv-content/$1
Admin PHP pages^dv-admin/([a-z0-9_-]+).php → src/dv-admin/$1.php
Admin clean URLs^dv-admin/([a-z0-9_-]+)/?$ → src/dv-admin/$1.php
Admin index^dv-admin/?$ → src/dv-admin/index.php
REST API^dv-api/(.*) → src/dv-includes/rest-api.php?_dv_route=$1
XML sitemap^sitemap.xml$ → src/dv-includes/sitemaps.php
Front-end router^(.*)$ → index.php [L,QSA] (if not a real file/dir)

Extending via filters

// Add rules inside the <IfModule> block
dv_add_filter('dv_htaccess_managed_content', function($rules) {
    return $rules . "\n    RewriteRule ^old-url$ /new-url [R=301,L]\n";
});

// Add rules outside the managed block
dv_add_filter('dv_htaccess_content', function($content) {
    return $content . "\n# Custom security header rules\nHeader set X-Frame-Options DENY\n";
});
Site health
Diagnostic checks — dv-includes/site-health-engine.php — accessible via Admin → Tools

Built-in checks

CheckRequirement
PHP version8.1 or higher
Database connectionActive and reachable
PDO extensionspdo_mysql + pdo_sqlite drivers loaded
mbstring extensionRequired for Unicode string handling
File permissionsconfig, content/, uploads/ writable
.htaccess writabilityRequired for permalink updates
Session configurationSecure cookie settings active
Disk spaceAvailable storage on uploads partition
Extensions can register their own health checks using the dv_get_site_health action hook — append to the $results array passed as the first argument.
Admin pages reference
All dashboard pages with their URLs and required capabilities
PageURLCapabilityDescription
Dashboard/dv-admin/index.phpStats overview and quick actions
Posts/dv-admin/posts.phpedit_postsCreate and manage posts and pages
Media/dv-admin/media.phpupload_filesMedia library with grid and list views
Categories/dv-admin/categories.phpmanage_categoriesManage taxonomy terms
Comments/dv-admin/comments.phpmoderate_commentsApprove, edit, delete user comments
Users/dv-admin/users.phpmanage_usersManage user accounts and roles
Profile/dv-admin/profile.phpEdit own profile and password
Menus/dv-admin/menus.phpmanage_menusBuild and assign navigation menus
Widgets/dv-admin/components.phpmanage_widgetsManage sidebar widget assignments
Customizer/dv-admin/visuals.phpmanage_optionsLive preview visual theme settings
Preferences/dv-admin/preferences.phpmanage_optionsGeneral, permalink, privacy settings
Extensions/dv-admin/extensions.phpmanage_extensionsInstall, activate, manage extensions
Layouts/dv-admin/layouts.phpmanage_layoutsInstall and switch layouts
Tools/dv-admin/tools.phpmanage_optionsSite health, import/export tools
Upgrade/dv-admin/upgrade.phpmanage_optionsCMS upgrade manager