-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Modules are self-contained PHP files dropped into the plugins/Dashboard/modules/ folder. This guide covers everything needed to create one.
- File Structure
- Required File Header
- Security Guard
- Unique ID Scoping
- JavaScript
- Localisation (i18n)
- Full Minimal Example
- Publishing to the Module Store
- Quick Checklist
A minimal module is a single PHP file. A module with language support adds a subfolder:
plugins/Dashboard/modules/
mymodule.php ← required
mymodule/ ← optional, for lang files
lang/
en_US.php
es_ES.php
.htaccess ← recommended to protect lang folder
Every module must begin with a PHP docblock header:
<?php
/**
* Module Name: My Module
* Module ID: mymodule
* Description: A short description of what this module does.
* Version: 1.0
* Default W: 4
* Default H: 2
*/| Field | Type | Required | Description |
|---|---|---|---|
Module Name |
string | Yes | Human-readable display name shown in the module list. |
Module ID |
string | Yes | Unique identifier. Must match the filename without .php. Lowercase, no spaces. |
Description |
string | Yes | Short description shown in the Module Store. |
Version |
string | Yes | Semantic version number, e.g. 1.0 or 2.1.3. |
Default W |
int | Yes | Default grid column width (1–12). 12 = full width. |
Default H |
int | Yes | Default grid row height. |
Immediately after the header, add the IN_GS guard. This prevents the file from being accessed directly via URL:
if (!defined('IN_GS')) { die('You cannot load this page directly.'); }Because multiple modules share the same page, CSS classes and element IDs can clash. Generate a unique ID prefix based on the module's file path:
$uid = 'mod_' . substr(md5(__FILE__), 0, 6);Use this prefix throughout the module:
// CSS — scope all rules to the unique wrapper ID
#<?php echo $uid ?> .my-class { color: red; }
// HTML — apply uid as the outer wrapper's id
<div id="<?php echo $uid ?>">
// HTML — suffix element IDs to avoid clashes
<div id="<?php echo $uid ?>-chart">
// JavaScript — look up elements by scoped ID
document.getElementById("<?php echo $uid ?>-chart");Note
Never use the Module ID as a DOM id. GridStack sets id="[ModuleID]" on the outer grid wrapper — reusing it will overwrite that element.
Modules are injected into the page after load, so DOMContentLoaded has already fired and will never trigger. Run code immediately or use setInterval.
Wrap all JavaScript in an IIFE to prevent global variable leakage:
<script>
(function() {
var el = document.getElementById("<?php echo $uid ?>-output");
function render() {
el.textContent = new Date().toLocaleTimeString();
}
render();
setInterval(render, 1000);
})();
</script>Warning
Never use DOMContentLoaded inside a module — it will not fire because modules load after the page is already ready.
Modules can ship their own language files. The Dashboard plugin provides a helper function that loads the lang file and returns a retrieval function.
// At the top of your module, after the IN_GS guard:
$i18n_m = dash_module_i18n('mymodule');Then use it anywhere in the module:
// In PHP expressions
$label = $i18n_m('lang_Title');
// Inline in HTML
<?php echo $i18n_m('lang_Title'); ?>
// Inside a PHP array
'label' => $i18n_m('lang_Pages'),Place lang files at plugins/Dashboard/modules/mymodule/lang/{locale}.php. The filename must match the locale code set in GS site settings, e.g. en_US.php:
<?php
// plugins/Dashboard/modules/mymodule/lang/en_US.php
$i18n = [
'lang_Title' => 'My Module',
'lang_Empty' => 'No items found.',
'lang_Edit' => 'Edit',
];Note
The lang/ folder is optional. If no lang file exists or the locale is not found, the helper falls back to en_US.php automatically.
Add a .htaccess file inside mymodule/lang/ to prevent direct web access:
# plugins/Dashboard/modules/mymodule/lang/.htaccess
deny from allNote
The Module Installer preserves .htaccess files when extracting from a zip, so this will be correctly installed alongside the lang files.
<?php
/**
* Module Name: Hello World
* Module ID: helloworld
* Description: A minimal example module.
* Version: 1.0
* Default W: 4
* Default H: 2
*/
if (!defined('IN_GS')) { die('You cannot load this page directly.'); }
$i18n_m = dash_module_i18n('helloworld');
$uid = 'hw_' . substr(md5(__FILE__), 0, 6);
?>
<style>
#<?php echo $uid ?> .hw-box {
padding: 12px;
background: #f9f9f9;
border-radius: 6px;
font-size: 14px;
}
</style>
<div id="<?php echo $uid ?>">
<h3><?php echo $i18n_m('lang_Title'); ?></h3>
<div class="hw-box">
<?php echo $i18n_m('lang_Greeting'); ?>
</div>
</div>Modules are listed in a remote JSON database. For your module to be installable, the repository must follow this structure:
your-repo/
mymodule.php ← module entry point with valid header
mymodule/ ← optional subfolder
lang/
en_US.php
.htaccess
The installer identifies valid modules by looking for the Module ID header in top-level .php files. All files belonging to the module subfolder are extracted automatically, including lang files and .htaccess.
Note
Only GitHub repository URLs are accepted by the installer for security reasons.
- Header block with all required fields
-
IN_GSsecurity guard - Unique ID:
$uid = 'abc_' . substr(md5(__FILE__), 0, 6) - All CSS scoped to
#uidwrapper - All element IDs prefixed with
$uid - JavaScript wrapped in an IIFE
- No
DOMContentLoadedlisteners - i18n:
$i18n_m = dash_module_i18n('mymodule')(if using lang files) - Lang files in
mymodule/lang/{locale}.php -
.htaccessinlang/folder to block direct access