Directory Structure

The full annotated directory tree for a Pubvana installation. Understanding where things live prevents misplacement of extension files.

/var/www/html2/                   ← project root (NOT web root)
├── app/                          ← CI4 application layer
│   ├── Config/                   ← framework + app configuration
│   │   ├── App.php               ← base URL, locale, indexPage
│   │   ├── Auth.php              ← Shield config, view overrides
│   │   ├── Constants.php         ← PLUGINS_PATH, THEMES_PATH, WIDGETS_PATH
│   │   ├── Events.php            ← pre_system: PluginManager::boot()
│   │   ├── Filters.php           ← filter aliases, global/route filters
│   │   ├── Pager.php             ← bootstrap_full pager template
│   │   ├── Routes.php            ← all core routes
│   │   └── Services.php          ← ThemeService, PluginManager registrations
│   ├── Controllers/
│   │   ├── Admin/                ← admin panel controllers
│   │   └── …
│   ├── Database/Migrations/      ← core schema migrations
│   ├── Filters/                  ← AdminFilter, MaintenanceFilter, TotpFilter
│   ├── Helpers/                  ← cms_helper.php, theme_helper.php
│   ├── Interfaces/PluginInterface.php
│   ├── Language/                 ← en/, es/, fr/, id/, pt/, sk/
│   ├── Models/                   ← core CI4 models
│   ├── Services/                 ← ThemeService, PluginManager, WidgetService…
│   ├── Commands/                 ← Spark CLI commands
│   └── Views/                    ← admin PHP views, auth overrides
├── plugins/                      ← all plugins live here
│   └── {PluginName}/
├── themes/                       ← all themes live here
│   └── {folder}/
├── widgets/                      ← all widgets live here
│   └── {folder}/
├── public/                       ← WEB ROOT — Apache DocumentRoot
│   ├── index.php
│   ├── .htaccess
│   └── assets/
├── writable/                     ← runtime-writable (chmod 777)
│   ├── cache/
│   ├── logs/
│   ├── uploads/
│   ├── backups/
│   └── updates/
├── docs/
├── tests/
├── vendor/                       ← Composer (not in git)
├── composer.json
├── spark
└── .env

Key Constants

Defined in app/Config/Constants.php:

define('PLUGINS_PATH', ROOTPATH . 'plugins/');
define('THEMES_PATH',  ROOTPATH . 'themes/');
define('WIDGETS_PATH', ROOTPATH . 'widgets/');

Use these constants — never hardcode paths — so installations in non-standard locations work correctly.

Web Root Isolation

public/ is the only directory exposed to the web. The .htaccess in public/ routes all requests through index.php. If your plugin needs publicly-accessible assets (CSS, JS, images), place them in public/plugins/{slug}/ and reference via base_url('plugins/myslug/css/style.css').

writable/ Permissions

The writable/ directory and all subdirectories must be writable by the web server user:

chmod -R 777 writable/

If your plugin creates its own writable directories (e.g., writable/myplugin/), do this in Installer::up() using mkdir() with a mode of 0777.

vendor/ and Composer

vendor/ is not committed to git. If you add Composer dependencies to a plugin, document the composer require command in your plugin's README. Pubvana does not run composer install automatically for plugins — dependencies must be bundled or the host must run Composer manually.