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.