Milk Admin

Installation / Update

This documentation provides a description of the installation and update system.

Initial Tutorial

If you want to customize the system for your project, you can release your versions and install them on new machines or update already installed versions for your clients

In this short tutorial we see how to create a new version of the system

1. First of all we need to make a modification. Go inside /Modules/posts/posts.object.php and add a field

$this->rule('new_field', [
            'type' => 'text',
            'label' => 'New Field'
        ]);

Now we need to update the module

Go to the module and modify or add the version property. If you update the version it must be greater than the previous one. I suggest for this system dates AAMMGG (year, month, day), but you can also put a progressive number and that's it. The system does not manage classic versions like 1.0, 1.0.1, 1.3.23.2 etc...

 protected $version = 251001;

A this point go to the admin and click the menu on the left installation. The module will update automatically.
Alternatively from shell you can run:

php milkadmin/cli.php {module_name}:update

Alternatively you can create a new version of the entire system.

1. Now from the shell going to the project directory type:

php milkadmin/cli.php build-version

Quick ZIP Package: You can use the zip parameter to automatically create a compressed package ready for deployment:

php milkadmin/cli.php build-version zip

This command will:

  • Create a new version folder with all the necessary files
  • Generate a ZIP archive containing milkadmin, milkadmin_local, and public_html folders
  • Add an install_from_zip.php script for easy server deployment
  • Clean up the original folders, leaving only the ZIP and install script

The resulting package can be uploaded to your server. Simply upload both the ZIP file and install_from_zip.php to your server directory, then access install_from_zip.php via browser. It will automatically extract the ZIP, redirect to the installation page, and self-delete.

Updating Paths and URL After Moving Installation

When you move your installation to a new directory or deploy to a different server/domain, you need to update the configuration paths and base URL. Use the update-paths CLI command:

# Update only directory paths (automatic detection)
php milkadmin/cli.php update-paths

# Update paths AND change the base URL
php milkadmin/cli.php update-paths "http://localhost/new-path/public_html/"

# Example: deploying to production
php milkadmin/cli.php update-paths "https://www.mysite.com/admin/"

This command updates:

  • public_html/milkadmin.php - MILK_DIR and LOCAL_DIR paths
  • milkadmin_local/config.php - base_url configuration
When to use update-paths
  • After moving the installation directory on the same server
  • After deploying to a new server or domain
  • After changing the public URL structure
  • After copying the installation to a different location

2. Alternatively, create a zip manually:

zip -r new_version.zip new_version_xxxxx

3. Open the page ?page=install and upload the zip file. The installation procedure will be executed. Verify that the new column has been created in the database and that the installation procedure now shows the new version.

Introduction

Installation, like updating, is designed for the entire system, not for individual modules. The idea is that, once a package is created with custom modules and configurations, it follows its own update path.

If individual modules have been written respecting the structure of abstract classes, it is possible to install, update and remove individual modules via shell.

php milkadmin/cli.php {module_name}:install 
php milkadmin/cli.php {module_name}:update
php milkadmin/cli.php {module_name}:uninstall

During development after creating a new version, you can execute the command:

php milkadmin/cli.php build-version

This command creates a folder inside the root directory with a copy of the system ready for installation. You can pass a parameter to set the version manually. Just check the permissions and owner of the files. The following instructions should be approximate

sudo chown -R www-data:www-data {new_version_xxxxx}
    mv {new_version_xxxxx}/ ../  
    cd .. 
    

Versions

Versions are indicated in the configuration file and are composed of 6 characters: AAMMGG where AA is the year e.g. 24, MM the month e.g. 01, GG the day e.g. 09.

Inside ito_class/setup.php the version number of the new installation is set.

define ('NEW_VERSION', '240901');

This is important because inside the various modules you can check the current version and the new version to understand whether you need to load the file with the hooks for the install or not

Hooks::set('init', function($page) {
// If there is no version it means that the system has yet to be installed
if (Config::get('version') == null || NEW_VERSION > Config::get('version')) {
   require_once (__DIR__.'/auth.install.php');
}
});

The Module

The installation module manages the various processes, but it is the responsibility of individual modules to install and update their tables and configurations.

For example, the Auth module installs and updates multiple tables. In this case it was necessary to rewrite the installation/update function.

public function installExecute() {
    if (!$this->model->buildTable()) {
        // error
    }
    $model2 = new SessionModel();
    if (!$model2->buildTable()) {
        // error
    }
    $model3 = new LoginAttemptsModel();
    if (!$model3->buildTable()) {
       // error
    }
    $user_id = Get::make('Auth')->saveUser(0, 'admin', 'admin@admin.com','admin', 1, 1 );
}

Inside the abstractModel there is the build_table function that creates the table if it doesn't exist or updates it if it exists. The table structure is set according to the abstractObject.
For example a very simple table could be structured like this

class PageModel extends AbstractModel {
    public function initRules() {
        $this->rule('id', [
            'type' => 'int',
            'primary' => true,
        ]);
        
        $this->rule('title', [
            'type' => 'string',
            'length' => 255,
            'nullable' => false,
            'label' => 'Title'
        ]);
       
    }
}

Config.php

the config file is rewritten by the installation procedure, however an initial config file must be created for the installation. This is generated by build-version by copying the example inside Assets/installation_config_example.php

Installation Management

Installation is managed through a series of hooks.

Hooks::run('install.get_html_modules', $html, $this->errors);

Here all modules can add their html to print. For example, data is requested to connect to the database. If there are errors they are passed as a parameter.

Hooks::set('install.get_html_modules', function($html, $errors) {
    ob_start();
    Form::input('text', 'connect_ip', 'IP Address', $_REQUEST['connect_ip'] ?? '127.0.0.1', $options);
    return ob_get_clean();
}

Hooks::run('install.check_data', $errors, $data);

After the data is sent from the form, it is verified. If there are errors they are passed as a parameter. otherwise we proceed to execute the installation.

Hooks::set('install.check_data', function($errors, $data) {
    $mysql_errors = [];
    if (empty($data['connect_ip'])) {
        $mysql_errors['connect_ip'] = 'IP Address is required';
    }
    return $mysql_errors;
}

Hooks::run('install.execute_config', $data);

Here modules can execute their installation code. For example create database tables or save data in the configuration

use Modules\Install\Install;
    Hooks::set('install.execute_config', function($data) {
    $default_data = [
        'base_url' =>$data['connect_ip']
    ];
    Install::setConfigFile('', $default_data);
    return $data;
}

Hooks::run('install.done', $html);

Here modules can add code to print at the end of the installation. For example a welcome message.

Hooks::set('install.done', function($html) {
    $html .= "Add new text here
'; return $html; }, 30);

Hooks::run('install.update', $data);

For the update there is a specific hook. Here modules can update database tables or do other necessary operations.

Hooks::set('install.update', function() {
    // modify the config file
    $config = file_get_contents(MILK_DIR."/config.php");
    $version = Config::get('version'); 
    $config = str_replace($version, NEW_VERSION, $config);
    File::putContents(MILK_DIR."/config.php", $config);
}, 100);

Install.class.php

To help the installation phases there is an install.class.php class that contains a series of utility functions.

setConfigFile($title, $configs)

Sets the parameters of the config file. The title is saved as a comment in the config file.

Hooks::set('install.execute', function($data) {
    $default_data = [
        'base_url' =>$data['connect_ip']
    ];
    Install::setConfigFile('', $default_data);
}

If you want to save a variable as a comment just name it with three underscores before its name

Hooks::set('install.execute', function($data) {
    $my_data = [
        '___my_data' => 'This is a comment variable'
    ];
});

This if the variable is a number or boolean or you want to add a comment next to the variable

Hooks::set('install.execute', function($data) {
    $auth_data = [
    'auth_expires_session' => ['value'=>'60*24*30','type'=>'number','comment' => 'Session duration in minutes'],
];
    Install::setConfigFile('', $auth_data);
}

executeSqlFile($file)

Executes an sql file. $file is the absolute path of the sql file to execute.

Hooks::set('install.execute', function($data) {
        Install::executeSqlFile(__DIR__.'/assets/mysql-install.sql');
        return $data;
    }, 20);

Adding a config parameter during installation

If you want to add a parameter to config.php during installation you can insert it by default inside modules/install/installer/default.install.php inside Hooks::set('install.execute', function($data)

If the parameter is only used in one module you can create a module.install.php file and insert

Hooks::set('install.execute', function($data) {
        Config::set('myparam', 'myvalue');
    });

Then inside the module insert

Hooks::set('init', function($page) {
        // If there is no version it means that the system has yet to be installed
        if (Config::get('version') == null || NEW_VERSION > Config::get('version')) {
            require_once (__DIR__.'/module.install.php');
        }
    }

CLI Commands for Modules

Individual modules can register their own CLI commands using the CLI hooks system. This allows modules to provide install, uninstall, and custom commands that can be executed from the command line.

Setting up CLI hooks

To enable CLI commands for your module, register the setup function in your module:

// Set up CLI commands
Hooks::set('cli-init', 'my_module_setup_cli_hooks', 90);

Implementing the CLI setup function

Create a function that registers your CLI commands:

function myModuleSetupCliHooks() {
    // Register CLI commands
    Cli::set("my_module:install", 'my_module_shell_install');
    Cli::set("my_module:uninstall", 'my_module_shell_uninstall');
    Cli::set("my_module:my_command", 'my_module_shell_my_command');
}

Implementing CLI command functions

Create the actual functions that will be executed when the CLI commands are called:

function myModuleShellInstall() {
    if (Cli::isCli()) {
        Cli::echo("Installing module: My Module");
        Cli::success('Module My Module install command executed');
        return true;
    }
}

function myModuleShellUninstall() {
    if (Cli::isCli()) {
        Cli::echo("Uninstalling module: My Module");
        Cli::success('Module My Module uninstall command executed');
        return true;
    }
}

function myModuleShellMyCommand() {
    if (Cli::isCli()) {
        Cli::echo("My custom command executed successfully!");
        Cli::success("Command completed");
    }
}

Using CLI commands

Once registered, you can execute the commands from the shell:

php milkadmin/cli.php my_module:install
php milkadmin/cli.php my_module:uninstall
php milkadmin/cli.php my_module:my_command

Always check if the script is running in CLI mode using Cli::isCli() before executing CLI-specific code. Use Cli::echo() and Cli::success() for proper CLI output formatting.

Updating Modules or the System

Updating the system calls the update hook for all modules. If you update just one module, only that module's update hook is called.

If you're using the AbstractModule, simply change the $version variable to notify the system that the module needs to be updated. If you're not using the AbstractModule, you need to add the module version to the configuration. You can do this dynamically by adding the following code:

Config::append('module_version', [$this->page => ['version'=>$this->version, 'folder'=>$folder]]);

folder is the folder or the filename if the module consists of a single file. You must enter the relative path to the modules folder.

Update

To update the system it is possible to do it using various ways.

Go to the installation page ?page=install. The system will check for a new update and install it if necessary.

inside ?page=install you find the page to upload the zip with the new updated version

You can however also use git and update the files directly. In this case when you reload the page ?page=install it will notice that a new update has been loaded and will execute the various update procedures of the individual modules so as to update any tables or other.

Be careful because if during the update the structure of a table is changed, it will be modified without asking for confirmation. In this case it is therefore the duty of individual modules to verify that any variations or removals of columns do not generate data loss

Loading...