Developer Docs

Welcome to the Cypht developer documentation. This guide covers everything you need to know about contributing to Cypht, from understanding the codebase structure to creating modules and debugging. Cypht is built entirely with a modular architecture using PHP and JavaScript. Every feature is implemented as a module that can be enabled or disabled independently, making it highly extensible and customizable.
Understanding the Cypht folder structure is essential for navigating and modifying the codebase effectively.
.github

CI/CD configuration files, templates for pull requests, issues, and bug reports. Automates testing and Docker image building during merge requests.

Config

Configuration files that work with .env files. Contains grouped settings with default values. Dynamic.php is auto-generated from .env changes.

language

Translation files for internationalization. Each file uses a 2-digit language code and returns an array of translated strings.

lib

Contains the core framework code of Cypht. Essential classes and utilities that power the entire application.

modules

All application modules. Each module contains setup.php, modules.php, site.js, site.css, and potentially an assets folder.

scripts

Utility scripts for configuration generation, user management, database operations, and development tasks.

site

Production files generated by scripts/config_gen.php. Contains optimized and minified assets for deployment.

tests

PHPUnit and Selenium test suites for automated testing and quality assurance.

third_party

Minified third-party libraries and dependencies used by Cypht.

.env

High-level configuration file. Check the Config folder for detailed explanations of all available variables.

Cypht's modular design is the foundation of its extensibility. Each module is self-contained and can be enabled or disabled independently.

Each module contains the following files:

  • site.js: Module-specific JavaScript code
  • site.css: Module-specific CSS styles
  • setup.php: Module configuration, pages, handlers, and allowed variables
  • modules.php: Handler and output classes
  • assets/: Fonts, images, and other resources (optional)

Modules should be designed to be as self-contained as possible. It's acceptable to depend on the core module, but avoid dependencies between non-core modules. If you need functionality from another module, consider if you're building it in the right place.

Module Encapsulation

Avoid require/include lines for files from another module at all costs. Use module execution order ("after" or "before") to augment functionality without direct dependencies.

Cypht supports two types of pages: basic pages (accessible via URL) and AJAX pages (load asynchronously).

To create a page called "list_messages":

In module/setup.php:

setup_base_page('all_messages', 'core');

add_handler('all_messages', 'load_messages', true);
add_output('all_messages', 'print_messages', true);

In module/modules.php:

class Hm_Handler_load_messages extends Hm_Handler_Module {
    public function process() {
        // Logic to get messages
        $this->out('messages', $message_list);
    }

}

class Hm_Output_print_messages extends Hm_Output_Module {
protected function output() {
$messages = $this->get('messages');
return '<div class="message_list">' . implode('', $messages) . '</div>';
}
}

For AJAX pages that load data asynchronously:

setup_base_ajax_page('ajax_load_new_messages', 'core');

add_handler('ajax_load_new_messages', 'get_new_messages', true);
add_output('ajax_load_new_messages', 'print_new_messages', true);
Page Authorization

Don't forget to add your new pages to the 'allowed_pages' array in your module's setup.php return statement.

Add this code in module/site.js to run your AJAX page every 15 seconds:

$(function() {
    if (hm_page_name() === 'all_messages') {
        setInterval(function() {
            Hm_Ajax.request(
                [{'name': 'hm_ajax_hook', 'value': 'ajax_load_new_messages'}],
                function(res) {
                    if (res.ajax_messages) {
                        // Append new messages to the list of messages
                        $('#messages_list').append(res.messages);
                    }
                }
            );
        }, 15000);
    }

});
AJAX Note

If setup_base_ajax_page does not have output modules, the values returned with $this->out() will be accessible in res in JavaScript.

Finally, add the following code to module/setup.php:

return array(
    'allowed_pages' => array(
        ...
        'ajax_load_new_messages',
        'all_messages'
    ),
    'allowed_get' => array(...),
    'allowed_output' => array(
        ...
        'ajax_messages'
    ),
    'allowed_post' => array(...)

);
  • Add all_messages and ajax_load_new_messages to the list of allowed pages
  • Add ajax_messages to the list of allowed outputs
  • Add post/get variables if they exist in the list of allowed get/posts
Debug Info

A page can have multiple handlers/outputs. A full list of all handlers and outputs attached to a page can be seen by accessing ?page=info page on your instance in the configuration map section.

Understanding the separation between handlers (logic) and outputs (presentation) is key to Cypht development.
Handler Modules

Contain business logic, form validation, data processing, and prepare data for output. Extend Hm_Handler_Module and implement the process() method.

Output Modules

Generate HTML and handle presentation. Extend Hm_Output_Module and implement the output() method. Use $this->trans() for translations.

  1. Handlers process first and can pass data using $this->out('key', 'value')
  2. Outputs run after handlers and can access data using $this->get('key')
  3. Outputs return HTML that gets combined to form the final page
Cypht supports multiple languages with a simple translation system.

In output modules:

$this->trans("Your text here");

Or with specific language:

hm_trans("Your text here", "en");
  1. Duplicate en.php in the language folder
  2. Rename using 2-digit ISO 639 code
  3. Modify interface_lang and interface_direction
  4. Add to interface_langs() function
  5. Update Hm_Test_Core_Output_Modules::test_lingual_setting test
Cypht includes comprehensive test suites using PHPUnit and Selenium to ensure code quality and reliability.

Run all tests:

php vendor/phpunit/phpunit/phpunit --configuration tests/phpunit/phpunit.xml

Run specific tests:

php vendor/phpunit/phpunit/phpunit \

--configuration tests/phpunit/phpunit.xml \
 --filter classOrMethodName
  1. Install Python and required packages: pip install -r tests/selenium/requirements.txt
  2. Run tests: sh tests/selenium/runall.sh

Check console output for failure details, click file paths in IDE to navigate to problematic lines, and review recent changes to classes or logic.

Effective debugging techniques for Cypht development.
  • Add var_dump() and exit() in your code
  • Use browser developer tools → Network tab
  • Filter by Fetch/XHR to see AJAX requests
  • Click requests to inspect preview/response

If you add a link to the left menu but don't see it, Cypht caches menus. Click the reload link below the navigation menu to refresh.

Guidelines for integrating third-party libraries and maintaining compatibility with existing integrations.
  1. Copy the minified file to the third-party directory
  2. Add the file path in Hm_Output_page_js.output or Hm_Output_header_css.output if it is a CSS file
  3. Finally, add the file in the combine_includes function in scripts/config_gen.php so that it is added when generating the production site

For JavaScript files:

Hm_Output_page_js.output

For CSS files:

Hm_Output_header_css.output

Edit .env file and add your module to CYPHT_MODULES variable:

CYPHT_MODULES=core,imap,smtp,your_module_name

In the modules folder, you'll find a hello_world module with the necessary scaffolding for creating a new module. Customize your module by following the code explained above.

Let's create a complete test page step by step:

Step 1: Add the page in core/setup.php:

setup_base_page('test');

Step 2: Access the page at ?page=test - you'll see "Page Not Found!" which is normal because we need to authorize it.

Step 3: Authorize the page in core/setup.php:

return array(
    'allowed_pages' => array(
        ...
        'test'
    ),
    'allowed_output' => array(...),
    'allowed_cookie' => array(...),
    'allowed_server' => array(...),
    'allowed_get' => array(...),
    'allowed_post' => array(...)

);

Step 4: Add content with outputs in core/setup.php:

add_output('test', 'test_heading', true, 'core', 'content_section_start', 'after');

Step 5: Define the output class in core/modules.php:

class Hm_Output_test_heading extends Hm_Output_Module {
    protected function output() {
        return '<div class="content_title">'.$this->trans('Test').'</div>';
    }

}

Step 6: Add more content with additional outputs:

add_output('test', 'test_first_div', true, 'core', 'test_heading', 'after');
class Hm_Output_test_first_div extends Hm_Output_Module {
    protected function output() {
        return '<div class="mt-3 col-lg-6 col-md-12 col-sm-12">
            <div class="card">
                <div class="card-body">
                    <div class="card_title">
                        <h4>'.$this->trans('Test').'</h4>
                    </div>
                    '.$this->trans('We are just testing').'
                </div>
            </div>
        </div>';
    }

}
Integration Compatibility

Cypht is actively used as embedded webmail in Tiki. Be careful with refactoring, module updates, layout changes, and interface modifications that might break upstream integrations.

The core module provides essential functionality that other modules depend on.
  • CSS Headers: Renders CSS headers and stylesheets
  • Alert Messages: Displays system alerts and notifications
  • JavaScript Loading: Loads JS files and defines shared functions
  • Basic Features: Configures backups, server pages, settings, etc.
Module Dependencies

Most modules will depend on the core module for basic functionality, but the core module is designed to work independently of all other modules.