Skip to content
Magal edited this page Jul 31, 2026 · 3 revisions

cmscli is a standalone PHP CLI application for managing CMSMS installations from the command line. It provides automation-friendly access to common administration tasks without requiring a web browser. Distributed as a self-contained .phar file with 70 built-in commands.

Modules can extend cmscli with their own commands using the clicommands capability.

Installation

  1. Download cmscli.phar from dev.cmsmadesimple.org
  2. Upload it to your CMSMS root directory on the server (use binary/SFTP mode, not ASCII)
  3. SSH into your server and run:
cd /path/to/your/cmsms
mv cmscli.phar cmscli
chmod +x cmscli
./cmscli --help

That's it. You run cmscli from inside your CMSMS root directory:

./cmscli site-info
./cmscli user-list
./cmscli module-list

# Or specify the CMSMS directory from elsewhere:
./cmscli --dir /path/to/cmsms site-info

System-wide Install (optional)

If you manage multiple CMSMS sites, place cmscli in your PATH:

mkdir -p ~/bin
cp cmscli.phar ~/bin/cmscli
chmod +x ~/bin/cmscli

# Use from any CMSMS root
cd /var/www/mysite
cmscli site-info

# Or specify the directory
cmscli --dir /var/www/mysite site-info

Global Options

Option Short Description
--dir <path> -d Specify CMSMS installation directory (default: current working directory)
--help -h Display help or command list
--nopermcheck -n Skip file ownership/permission checks
--usestderr Send error messages to stderr instead of stdout
--version -V Display cmscli version

Built-in Commands (70)

Cache

Command Description
cache-clear Clear the server cache

Configuration

Command Description
config-get <var> Get a CMSMS config variable value
config-set <var> <value> Set a CMSMS config variable

Content

Command Description
content-list List content pages (-b brief, -D design, -p parent, -T template, -t type, -a active, -i inactive, -H hierarchy, -m show-in-menu)
content-get <page> [property] Get a page property value. Defaults to content_en (-l list-props, -s smarty, -f file)
content-create <title> Create a new content page (-p parent, -a alias, -T template, -t type, -c content, -f file, -m menutext, -I inactive, -M hide-from-menu)
content-edit <page> Edit page properties (--title, -a alias, -T template, -p parent, -m menutext, --active, --showinmenu, -c content, -f file, -P prop=value)
content-delete <page> Delete a page by alias or ID (-f force even with children)

Database

Command Description
db-dump Generate mysqldump of CMSMS tables (-f full DB, -F file output)
db-import Import SQL file into the CMSMS database (stdin)
db-reset Drop tables from the CMSMS database

Designs

Command Description
design-list List designs (-b brief)
design-export <name> Export design as XML (-f file)
design-import <file> Import design from XML file

Events

Command Description
event-list List system and module events with handler counts (-b brief, -o originator)
event-send <Originator::EventName> Trigger an event (-p key=value for parameters, repeatable)

GCBs (Global Content Blocks)

Command Description
gcb-list List Global Content Blocks (-b brief)
gcb-get <name> Get GCB content (-f file)
gcb-set <name> Set/update GCB content from file or stdin (-f file, -c create)

Groups

Command Description
group-add <name> Create a new admin group
group-del <name> Remove an admin group
group-list List admin groups
group-perm-list <group> List permissions for a group

Jobs (CmsJobManager)

Command Description
jobs-list List queued background jobs with status (-b brief)
jobs-run Process pending background jobs (-f force, -v verbose)

Logs (Admin Log)

Command Description
log-list List admin log entries (--limit, -m source, -u user, -s search, -b brief)
log-add <source> <action> Add an audit log entry (-i item-id, -u user-id)
log-clear Clear log entries (--older-than, -m module, --all)

Mail

Command Description
mail-info Show mailer configuration
mail-test <email> Send a test email (-s subject, -B body)

Modules

Command Description
module-activate <name> Activate a module
module-deactivate <name> Deactivate a module
module-hide <name> Hide a module from admin panel
module-unhide <name> Un-hide a module
module-install <name> Install a module (--forge download from Forge, --with-deps resolve dependencies)
module-install-xml <file> Install a module from a local XML package file
module-uninstall <name> Uninstall a module, keep files on disk (-f force)
module-purge <name> Uninstall a module and delete its files (-f force)
module-upgrade <name> Upgrade a module to latest version
module-list List installed modules (-b brief)
module-list-hidden List hidden modules
module-package-xml <name> Package module into XML distribution file (-o dir, --stdout)

Preferences

Command Description
pref-get [name] Get a site preference value (-l list all, -s search by pattern)
pref-set <name> <value> Set/create a site preference
module-pref-get <module> [name] Get a module preference via GetPreference() (-l list, -s search)
module-pref-set <module> <name> <value> Set a module preference via SetPreference()

Routes

Command Description
route-list List custom routes (-b brief, -m module)
route-add <pattern> <module> Add a static route (--absolute, --key2, --key3)
route-delete <pattern> Delete a static route (-m module)

Site

Command Description
site-info Display site name, version, schema version
site-down Set maintenance mode (-c complete shutdown)
site-up Remove maintenance mode
site-checksum-generate Generate checksum file (--core, --noindexes)
site-checksum-verify Verify installation integrity

Stylesheets

Command Description
stylesheet-list List stylesheets (-b brief, -D design)
stylesheet-export <name> Export stylesheet (-f file)
stylesheet-import <name> Import stylesheet (-f file, -c create)

Templates

Command Description
template-list List templates (-b brief, -D design, -t type)
template-export <name> Export template (-f file)
template-import <name> Import template (-f file, -c create)
template-file-export [name] Export template(s) to file-based editing in assets/templates/ (--all, -t type, -m module, -D design)
template-file-import [name] Import template(s) from file back into database (--all, -k keep file)

UDTs (User Defined Tags)

Command Description
udt-list List User Defined Tags (-b brief)
udt-export <name> Export UDT code (-f file)
udt-import <name> Import/update UDT code (-f file, -c create, -D description)

Users

Command Description
user-list List admin users
user-add <username> <email> <password> Create new admin user (-g group)
user-edit <username> Edit admin account details (--email, --firstname, --lastname)
user-password <username> <password> Change an admin user's password
user-activate <username> Activate an admin account
user-deactivate <username> Deactivate an admin account
user-del <username> Remove an admin user
user-group <username> <group> Add/remove user from groups (--add, --remove)

Meta (no CMSMS required)

Command Description
about Display installation/usage information
cmsms-latestversion Get latest CMSMS version number
cmsms-download Download latest CMSMS installation assistant

Usage Examples

Site Management

# Display site info
cmscli site-info

# Maintenance mode
cmscli site-down        # Standard maintenance page
cmscli site-down -c     # Complete shutdown (no output at all)
cmscli site-up

# Clear cache
cmscli cache-clear

# Integrity verification
cmscli site-checksum-generate           # All files
cmscli site-checksum-generate --core    # Core files only
cmscli site-checksum-verify

Configuration

cmscli config-get db_prefix       # Output: cms_
cmscli config-get root_url        # Output: https://www.mysite.com
cmscli config-set root_url https://www.mysite.com

Content Pages

# List pages
cmscli content-list                    # Full tabular list
cmscli content-list -b                 # Brief: aliases only
cmscli content-list -p home            # Children of 'home' page
cmscli content-list -t content         # Filter by type
cmscli content-list -T "My Template"   # Filter by template
cmscli content-list -a                 # Active pages only

# Get page content
cmscli content-get home                # Default: outputs content_en
cmscli content-get home extra1         # Custom content block
cmscli content-get home -l             # List available properties
cmscli content-get home -s             # Process through Smarty first
cmscli content-get home -f output.html # Write to file

# Create pages
cmscli content-create "My New Page"
cmscli content-create "My New Page" -p home -T "Page Template"
cmscli content-create "My New Page" -c "<p>Hello</p>"
cmscli content-create "My New Page" -f content.html -I -M

# Edit pages
cmscli content-edit my-page --title "New Title"
cmscli content-edit my-page -T "Other Template"
cmscli content-edit my-page -c "<p>Updated</p>"
cmscli content-edit my-page -P extra1="Custom value"

# Delete pages
cmscli content-delete my-page
cmscli content-delete my-page -f     # Force: delete even with children

Templates and Stylesheets

# List
cmscli template-list
cmscli template-list -D "My Design"    # Filter by design
cmscli template-list -t __CORE__::page  # Filter by type
cmscli stylesheet-list -b              # Brief: names only

# Export/Import
cmscli template-export "My Template" -f output.tpl
cmscli template-import "My Template" -f input.tpl
cmscli template-import "New Template" -f input.tpl -c   # Create new
cmscli stylesheet-export "My Stylesheet" -f output.css
cmscli stylesheet-import "My Stylesheet" -f styles.css

# File-based editing workflow
cmscli template-file-export --all              # Export all to assets/templates/
cmscli template-file-export -m News            # All News module templates
cmscli template-file-export -D "My Design"     # All in a design
# Edit .tpl files in your IDE...
cmscli template-file-import --all              # Import all back to database
cmscli template-file-import "My Template" -k   # Keep file after import

Designs

cmscli design-list
cmscli design-export "My Design" -f design.xml
cmscli design-import design.xml

UDTs and GCBs

# User Defined Tags
cmscli udt-list
cmscli udt-export my_tag -f my_tag.php
cmscli udt-import my_tag -f my_tag.php
cmscli udt-import new_tag -f code.php --create -D "My helper tag"

# Global Content Blocks
cmscli gcb-list
cmscli gcb-get footer -f footer.tpl
cmscli gcb-set footer -f footer.tpl
cmscli gcb-set "new block" -f block.tpl --create

Events and Preferences

# Events
cmscli event-list                    # All events
cmscli event-list -o Core            # Filter by originator
cmscli event-send Core::ContentPostRender
cmscli event-send MyModule::MyEvent -p key1=value1 -p key2=value2

# Preferences
cmscli pref-get sitename
cmscli pref-get --list               # List all preference names
cmscli pref-get --search sitename    # Search by pattern
cmscli pref-set sitename "My Site"

# Module Preferences
cmscli module-pref-get StoreFrontPayPal --list           # List all prefs for module
cmscli module-pref-get StoreFrontPayPal paypal_testmode  # Get single value
cmscli module-pref-get StoreFrontPayPal --search sandbox # Search by pattern
cmscli module-pref-set StoreFrontPayPal paypal_testmode 0

Routes

# List routes
cmscli route-list                    # All routes
cmscli route-list -m News            # Filter by module

# Add routes
cmscli route-add '/products/(?P<slug>[a-z0-9-]+)$' StoreFront
cmscli route-add '/api/callback' MyModule --absolute
cmscli route-add '/webhook' MyModule --key2 handler

# Delete routes
cmscli route-delete '/products/(?P<slug>[a-z0-9-]+)$'
cmscli route-delete '/test' -m News

Admin Log

# List log entries
cmscli log-list                          # Last 50 entries
cmscli log-list --limit 10              # Last 10
cmscli log-list --source Core           # Filter by source
cmscli log-list -m MeetSimple           # Short form
cmscli log-list --user 1                # Filter by user_id
cmscli log-list --search "password"     # Search action text
cmscli log-list -b                      # Brief: timestamp + action only

# Add log entry
cmscli log-add MyModule "Deployed new version"
cmscli log-add cmscli "Ran backup script" --item-id 42

# Clear log entries
cmscli log-clear --older-than 30              # Older than 30 days
cmscli log-clear --module Core                # All entries from source
cmscli log-clear --older-than 7 --module News # Combined
cmscli log-clear --all                        # Delete ALL

Module Management

# List modules
cmscli module-list
cmscli module-list -b                # Brief: names only
cmscli module-list-hidden

# Install from disk
cmscli module-install MyModule

# Install from Forge
cmscli module-install MyModule --forge

# Install with all dependencies resolved
cmscli module-install CGSmartImage --with-deps

# Install from local XML package
cmscli module-install-xml /path/to/MyModule-1.0.xml

# Lifecycle
cmscli module-activate MyModule
cmscli module-deactivate MyModule
cmscli module-upgrade MyModule
cmscli module-uninstall MyModule      # Keep files
cmscli module-purge MyModule          # Remove everything

# Package for distribution
cmscli module-package-xml MyModule
cmscli module-package-xml MyModule -o /tmp
cmscli module-package-xml MyModule --stdout    # Output XML to stdout

.distignore support: Place a .distignore file in your module root to exclude files from packaging (same syntax as .gitignore). Example:

.git
.kiro
releases/
tests/

The following are always excluded regardless of .distignore:

  • Git: .git, .gitignore, .gitattributes, .gitmodules, .gitkeep
  • AI agents: .kiro, .claude, .amazonq, .cursor, .copilot
  • IDEs: .vscode, .vs, .idea
  • Meta: .distignore

#### Database

```bash
cmscli db-dump                   # Dump to stdout
cmscli db-dump -F dump.sql       # Dump to file
cmscli db-dump -f                # Full database (not just CMSMS tables)
cmscli db-import < dump.sql      # Import from file

Users and Groups

# Users
cmscli user-list
cmscli user-add jdoe john@example.com password123
cmscli user-add jdoe john@example.com password123 -g "Editor"
cmscli user-edit jdoe --email new@email.com --firstname John
cmscli user-password jdoe newpassword
cmscli user-activate jdoe
cmscli user-deactivate jdoe
cmscli user-del jdoe
cmscli user-group jdoe "Editor" --add
cmscli user-group jdoe "Editor" --remove

# Groups
cmscli group-list
cmscli group-add "My Group"
cmscli group-del "My Group"
cmscli group-perm-list "Editor"

Background Jobs (CmsJobManager)

cmscli jobs-list                      # Full table with status
cmscli jobs-list -b                   # Brief: names only
cmscli jobs-run                       # Process if throttle allows
cmscli jobs-run --force               # Ignore frequency throttle
cmscli jobs-run --force --verbose     # Show per-job progress

Recommended cron entry for low-traffic sites:

*/3 * * * * cd /var/www/mysite && php cmscli.phar jobs-run --force 2>/dev/null

Common Automation Patterns

Backup script:

#!/bin/bash
cd /var/www/mysite
cmscli site-down
cmscli db-dump -F tmp/dump.sql
tar zcf ../backup-$(date +%Y%m%d).tar.gz .
cmscli site-up

Deploy workflow:

cd /var/www/mysite
cmscli site-down
git pull
cmscli cache-clear
cmscli template-file-import --all
cmscli site-up

File-based template editing:

# Export templates to disk for IDE editing
cmscli template-file-export --all

# Edit .tpl files in assets/templates/ with your editor...

# Import changes back to database
cmscli template-file-import --all

Adding CLI Commands to Your Module

Any module can contribute commands to cmscli by declaring the clicommands capability and implementing get_cli_commands().

Step 1: Declare the Capability

In your main module class, override HasCapability():

public function HasCapability($capability, $params = [])
{
    if ($capability == 'clicommands') return true;
    return parent::HasCapability($capability, $params);
}

Step 2: Return Command Instances

Implement get_cli_commands() to return an array of command objects:

public function get_cli_commands(\CMSMS\CLI\App $app)
{
    return [
        new MyModule\Commands\ItemListCommand($app),
        new MyModule\Commands\ItemExportCommand($app),
    ];
}

Step 3: Create Command Classes

Each command extends \CMSMS\CLI\GetOptExt\Command. Place them in your module's lib/Commands/ directory.

<?php
namespace MyModule\Commands;

use CMSMS\CLI\App;
use CMSMS\CLI\GetOptExt\Command;
use CMSMS\CLI\GetOptExt\Option;
use CMSMS\CLI\GetOptExt\GetOpt;
use GetOpt\Operand;

class ItemListCommand extends Command
{
    public function __construct(App $app)
    {
        parent::__construct($app, 'mymodule-item-list');

        $this->addOption(
            Option::Create('b', 'brief')
                ->setDescription('Output only item names')
        );
        $this->addOption(
            Option::Create('t', 'type', GetOpt::REQUIRED_ARGUMENT)
                ->setDescription('Filter by type')
        );
        $this->addOperand(
            Operand::create('pattern', Operand::OPTIONAL)
        );
    }

    public function getShortDescription()
    {
        return 'List items managed by MyModule';
    }

    public function getLongDescription()
    {
        return <<<EOT
Lists all items in MyModule. Use --brief for names only.
Use --type to filter by item type.
EOT;
    }

    public function handle()
    {
        $brief = $this->getOption('brief')->getValue();
        $type = $this->getOption('type')->getValue();
        $pattern = $this->getOperand('pattern')->getValue();

        $db = $this->getDb();

        $sql = 'SELECT * FROM ' . \CMS_DB_PREFIX . 'mod_mymodule_items';
        $params = [];
        if ($type) {
            $sql .= ' WHERE type = ?';
            $params[] = $type;
        }
        $rows = $db->GetArray($sql, $params);

        if (empty($rows)) return;

        if ($brief) {
            foreach ($rows as $row) {
                echo $row['name'] . "\n";
            }
            return;
        }

        $fmt = "%-6s %-40s %-20s\n";
        echo sprintf($fmt, 'ID', 'Name', 'Type');
        echo sprintf($fmt, '--', '----', '----');
        foreach ($rows as $row) {
            echo sprintf($fmt, $row['id'], $row['name'], $row['type']);
        }
    }
}

Command API Reference

Constructor

parent::__construct(App $app, string $command_name, bool $requires_cmsms = true);

The third parameter controls whether this command needs a CMSMS installation. Set to false only for commands that work independently (rare for module commands).

Available Methods in handle()

Method Returns Description
$this->getApp() App The cmscli application instance
$this->getDb() Connection The CMSMS database connection
$this->getOption('name') Option Get an option by name (call ->getValue() for the value)
$this->getOperand('name') Operand Get an operand by name (call ->getValue() for the value)

Options and Operands

// Flag (boolean option, no argument)
$this->addOption(Option::Create('v', 'verbose')->setDescription('Verbose output'));

// Option with required argument
$this->addOption(Option::Create('f', 'file', GetOpt::REQUIRED_ARGUMENT)
    ->setDescription('Output file path'));

// Option with optional argument and default
$this->addOption(Option::Create('l', 'limit', GetOpt::OPTIONAL_ARGUMENT)
    ->setDescription('Result limit')
    ->setDefaultValue('50'));

// Required operand
$this->addOperand(Operand::create('name', Operand::REQUIRED));

// Optional operand
$this->addOperand(Operand::create('filter', Operand::OPTIONAL));

Output and Error Handling

  • Write output to stdout with echo.
  • Throw \RuntimeException for errors (cmscli catches and displays them).
  • Exit codes: 0 for success (automatic), non-zero for errors (from exception code).
  • Design for automation: minimal output, machine-parseable where possible.

Naming Conventions

Prefix your command names with the module name or a short identifier to avoid collisions:

  • mymodule-item-list
  • mymodule-item-export
  • mymodule-sync

File Organization

modules/MyModule/
  lib/
    Commands/
      class.ItemListCommand.php
      class.ItemExportCommand.php
      class.SyncCommand.php

Use your module's namespace for the command classes and let CMSMS autoloading handle them.

Testing

Run your commands from the CMSMS root:

./cmscli --nopermcheck mymodule-item-list
./cmscli --nopermcheck mymodule-item-list --brief
./cmscli --nopermcheck mymodule-item-list --type article

Use --nopermcheck during development to skip file ownership checks.

Exit Codes

Code Meaning
0 Success
1+ Error (message printed to stdout or stderr)