Code snippets and guides for developers

Here you can find code snippets and documented procedures,
to get the idea how it works and how things can be achiveded at high level.

About this Guides

This Guide is currently on writting proces. It should cover framework usage from scratch, and it's based on an example that is being devolped following it.

Installation

This is the install for a empty setrill project, we download first_install zip, and exec the install setting all values on it's default values.

user@server:~/www$ mkdir mysite_domain
user@server:~/www$ cd mysite_domain

user@server:~/www/mysite_domain$ wget http://setrill.org/dms/downloads/setrill_0_107_new_install.zip

...

user@server:~/www/mysite_domain$ unzip setrill_0_107_new_install.zip 

...

user@server:~/www/mysite_domain$ php cron.php -m setrill -f install
Setrill Framework v0.107 shell call
Event OBJECT_SET_DEFAULT_FUNCTION registered!
Event OBJECT_INSTANCE_CREATED registered!
Event DB_OBJECT_INSTANCE_CREATED registered!
Event EX_OBJECT_INSTANCE_CREATED registered!
Event OBJECT_SET_TRIGGERS registered!
--- Setting default config
--- Performing a first install.
--- CONFIG values
file for overriding app class []: 
base url for accessing to server []: 
Server time zone setting [Europe/Berlin]: 
jquery version []: 
log directory [log/]: 
css personalization sufix []: 
enable mobile check on php side []: 
enable cache for static files []: 
append data to js static files to avoid cache issues []: 
append data to css static files to avoid cache issues []: 
seconds to set a notice on large query []: 
Devel flag []: 
Write sql log []: 
Show info on console log []: 
Reports @supressed messages []: 
adds use strict on all js static files []: 
Clean log file on each execution []: 
Clean mysql log file on each execution []: 
Write sql execution time []: 
log backtrace on mysql log []: 
log backtrace on error log []: 
Overwritte client ip []: 
log mysql for specified user []: 
force mobile output []: 
--- Creating config file.
--- OK
INFO: Installing module setrill

--- Creating a default empty config file
INFO: Module setrill installed

install main? [YES]: 
creating config dir (not found)
--- Creating a default empty config file
Your Application Name []: 
INFO: Module main installed

--- Installation finished

Assuming you have configured a web server, accessing to the url will give this output

Databases

Working with databases requires a module extending the database object.

SQL queries should never be build manually.

Installing the database module

Firstly we need to install the database module.

user@server:~/www/yoursite_domain$ ./cron.php -m setrill -f install --deploy http://setrill.org/dms/downloads/database_0_1.zip 
Setrill Framework v0.107 shell call
INFO: copying to cache/deploy/downloads/database_0_1.zip
Creating bak files
Creating directory modules/database/...Ok
installing modules/database/database.class.php
installing modules/database/database.def.php
creating config dir (not found)
--- Creating a default empty config file
WARNING: mysql submodule not found. A submodule extending database is needed
INFO: Module database installed
Removing bak files

The database module is not functional, needs a section to manage the database. We install de mysql section, that works with Mysql and MariaDB databases.

user@server:~/www/yoursite_domain$ ./cron.php -m setrill -f install --deploy http://setrill.local/dms/downloads/mysql_db_0_12.zip
Setrill Framework v0.107 shell call
INFO: copying to cache/deploy/downloads/mysql_db_0_12.zip
Creating bak files
Creating directory modules/database/mysql/...Ok
Creating directory modules/database/mysql/addons/...Ok
installing modules/database/mysql/mysql.class.php
installing modules/database/mysql/mysql_db.def.php
installing modules/database/mysql/addons/mysql.addon.php
creating config dir (not found)
--- Creating a default empty config file
DataBase Name []: mysite_domain
DataBase Server []: localhost
Database User []: user
Database Password []: ******
Database Prefix []: 
installing mysql.addon.php
INFO: Module mysql_db installed

creating Database connector..
DB conecion stablished
creating database mysite_domain
select database `mysite_domain`
Removing bak files

The Database is created. The database info is stored on config file for the module. At this moment the password is stored in plain text.

Creating a database object

The database object will always extend the CDBobject. The database and it's settings are defined on the module, as seen above

<?php
/** @file /application/modules/main/person.class.php **/

require_once ("classes/dbobject.class.php");

class CPerson extends CDBObject {
    var $person_id;
    var $person_name;

    var $_internal; // Not a DB field

    function __construct() { parent::__construct("persons", "person_id", array("person_id")); }

    function getDefinition() {
        $types = array("person_id" => "int(11)", "person_name" => "varchar(255)");
        $default = null;
        $collation = null; // not implemented yet
        $attributes = null;
        $index = null; // not implemented yet
        return array($types, $default, $collation, $attributes, $index);
    }
}

Usually, the database objects constructors do not have parameters. If needed, they need to consider overriding the CDBObject::createNew function

The constructor takes the table_name, the table_id (could be set to null) and an array of fields considered as numeric.

Each var of the object is considered as a table field. If not, the var must start with _

The getDefinition function must be overrided to specify the database fields.

<?php
/** @file /application/modules/main/cron/install_person.php **/

global $app;

require_once ($app->getModuleFile("person.class.php"));

$person = new CPerson();
$person->install();

The object must be installed using CDObject::install function (that will create the table on DB), usually placed on module install. see Module Maintenance - Installing

The output for this manual install is

user@server:~/www/yoursite_domain$ ./cron.php -f install_person
creating Database connector..
DB conecion stablished
select database `mysite_domain`
Setrill Framework v0.107 shell call
INFO: creating table for CPerson


Inserting data to database

<?php
/** @file /application/modules/main/cron/insert_person.php **/

global $app;

require_once ($app->getModuleFile("person.class.php"));

$person = new CPerson();

$person->set_value("person_name", "jaume");

if ($person->insert()->valid()) {
    $app->console_output(INFO, "data inserted", $person);
}

if ($person->has_error()) {
    $app->console_output(ERROR, "cannot insert", $person);
}

$person->print_data();

Although the value can be assigned directly, using CDObject::set_value function ensures that any actions related to it are triggered. This can include privileges, and other actions or checks over the data

The output for this code is

user@server:~/www/yoursite_domain$ ./cron.php -f insert_person
creating Database connector..
DB conecion stablished
select database `mysite_domain`
Setrill Framework v0.107 shell call
INFO: data inserted
==CPerson==
INTERNAL FLAGS: 100000 (100000000000000000000)
person_id: 1
person_name: jaume

If failed the output could be

ERROR (123): cannot insert
==CPerson==
INTERNAL FLAGS: 100000 (100000000000000000000)
ERROR CODE: 123
person_id: ""
person_name: jaume

Code 123 matches to OBJECT_DATABASE_ERROR, see database logging to get more info about what happened.

The flag shown in output (1 << 20), matches EMPTY_SEARCH_ALLOWED. see object flags to get more info about flags

The mysql sentence executed is

 INSERT INTO persons (`person_name`) VALUES ('Jaume');

To set data from a client, see ALTERNATIVE_ACTION_INSERT

Loading and updating data from database

<?php
/** @file /application/modules/main/cron/update_person.php **/

global $app;

require_once ($app->getModuleFile("person.class.php"));

$person = new CPerson();

if ($person->loadFromKey(1)->valid()) {
    $app->console_output(INFO, "person name is ".$person->get_value("person_name"));
    $person->set_value("person_name", "Jaume")->update();
}

if ($person->load_by("person_name", "Jaume")->valid()) {
    $app->console_output(INFO, "loaded #".$person->get_key_value());
}

using CDObject::get_value function ensures that any actions related to it are triggered. This can include privileges, and other actions or checks over the data.

The oputput for this code is

user@server:~/www/yoursite_domain$ ./cron.php -f update_person 
creating Database connector..
DB conecion stablished
select database `mysite_domain`
Setrill Framework v0.107 shell call
INFO: person name is jaume
INFO: loaded #1

The mysql sentences are

SELECT * FROM persons WHERE `person_id` = '1'    LIMIT 1; 
UPDATE persons SET  `person_name` = 'Jaume'  WHERE `person_id` = '1';
SELECT * FROM persons WHERE `person_name` = 'Jaume'    LIMIT 1;

To update data from a client, see ALTERNATIVE_ACTION_SAVE

Loading Multiple data

We need more data on our example.

<?php
/** @file /application/modules/main/cron/populate_person.php **/

global $app;

require_once ($app->getModuleFile("person.class.php"));

$person = new CPerson();

$person->reset()->set_value("person_name", "Marc")->insert();
$person->reset()->set_value("person_name", "Carles")->insert();
<?php
/** @file /application/modules/main/cron/load_persons.php **/

global $app;

require_once ($app->getModuleFile("person.class.php"));

$persons = new CPerson();

$app->console_output(INFO, "Persons Rows: " . $persons->count());

foreach ($persons->loadAll() as $person) {
    $app->console_output(INFO, "person name is ". $person->get_value("person_name"));
}

foreach ($persons->loadAll("*", array ("a" => "person_name", "b" => "Marc", "op" => "LIKE")) as $person) {
    $app->console_output(INFO, $person->get_value("person_name"). " has id #". $person->get_key_value());

Customs query conditions are automatically build.

Output for this example is

INFO: Persons Rows: 3
INFO: person name is jaume
INFO: person name is Marc
INFO: person name is Carles
INFO: Marc has id #2

The mysql sentences are

SELECT COUNT(*) as c FROM persons WHERE 1;
SELECT * FROM persons WHERE 1;
SELECT * FROM persons WHERE `person_name` LIKE 'Marc';

To serve data to a client see ALTERNATIVE_XML_GET and ALTERNATIVE_XML_LIST

Building queries

Queries are build using an array structure. In it's most basic form, it is formed by 'a', and 'b', where 'a' is the field and 'b' the value to match.

The operator can be added as 'op', and is inserted betwen 'a' and 'b'.

The operator can be

  • normal ones like '=', '<', '>', '<>', 'LIKE'
  • 'IS', or 'IS NOT', and then the 'b' should be set to "null" as string.
  • 'IN', in that case the b is a comma separated values string.
  • 'BETWEEN', in that case b is a string with two values separated by comma.
  • 'REGEXP'

'a' and 'b' can also be and struct following the same structure, so using OR as 'op' we can build something like ((a1 = b1) OR (a2 = b2))

We can also insert elements side by side using 'next', so we can build something like ((a1 = b1) AND (a2 = b2) AND ... AND (an = bn))

Using 'u' we can can set union as OR, to get something ((a1 = b1) OR (a2 = b2) OR ... OR (an = bn)), the operator is always set before the current current element

We can specify the name for the field with 'object' and 'objectb' for the value so the query could be like (a_name.a = b_name.b)

We can specify functions over value using 'func', and also over field as 'a_func'

And finally we can use 'a_raw' instead of 'a', and 'b_raw' instead of 'b' to set the value as is, without any checks, to avoid injection for example.

<?php
/** @file /application/modules/main/cron/query_example.php **/

global $app;

require_once($app->getModuleFile("person.class.php"));

$persons = new CPerson();

$w1 = array("a" => array("next" => array(array("a" => "person_name", "b" => "Jaume"),
                                           array("a" => "person_name", "b" => "Marc", "u" => "OR"),
                                           array("a" => "person_name", "b" => "Carles", "u" => "OR"))),
            "b" => array("a" => "person_name", "b" => "5,6", "op" => "IN", "a_func" => "LENGTH"), "op" => "AND");

$w2 = array("a" => array("a" => "person_name", "b" => "J%", "op" => "LIKE"),
            "b" => array("a" => "person_name", "b" => "Marc", "object" => "persons"), "op" => "OR");

$w = array("a" => $w1, "b" => $w2, "op" => "AND");

foreach ($persons->loadAll("*", $w) as $person) { $app->console_output(INFO, "matches #".$person->get_key_value()); }

if ($persons->has_error()) {
    $app->console_output(ERROR, "Error!", $persons);
}

Resulting in this query

SELECT * FROM persons WHERE 
(
    ((`person_name` = 'Jaume') OR (`person_name` = 'Marc') OR (`person_name` = 'Carles'))  AND 
    (LENGTH(`person_name`) IN ('5','6'))
) AND ((`person_name` LIKE 'J%') OR (`persons`.`person_name` = 'Marc'))

There is a set of static functions to help building the structure, getting a more clean code.

<?php
/** @file /application/modules/main/cron/query_example_w_statics.php **/

global $app;

require_once($app->getModuleFile("person.class.php"));

$persons = new CPerson();

$w = array("a" => CDBObject::data_query_multiple(array(CDBObject::data_query_field("person_name", "Jaume"),
                                                        CDBObject::data_query_field("person_name", "Carles", "=", "OR"),
                                                        CDBObject::data_query_field("person_name", "Marc", "=", "OR")),
                                                  "OR"),
           "b" => array("a" => "person_name", "b" => "5,6", "op" => "IN", "a_func" => "LENGTH"), "op" => "AND");

foreach ($persons->loadAll("*", $w) as $person) { $app->console_output(INFO, "matches #".$person->get_key_value()); }

if ($persons->has_error()) {
    $app->console_output(ERROR, "Error!", $persons);
}

The most insteresting part of this is the posibility to build queries dinamically.

Relationships

We will need another object, remember that it must be installed.

<?php
/** @file /application/modules/main/address.class.php **/

require_once ("classes/dbobject.class.php");

class CAddress extends CDBObject {
    var $address_id;
    var $address_name;
    
    function __construct() { parent::__construct("addresses", "address_id", array("address_id")); }

    function getDefinition() {
        $types = array("address_id" => "int(11)", "address_name" => "varchar(255)");
        $default = null;
        $collation = null; // not implemented yet
        $attributes = null;
        $index = null; // not implemented yet
        return array($types, $default, $collation, $attributes, $index);
    }
}

$app->store_object(new CAddress);

We will also modify our person class to add a link to an address

<?php
/** @file /application/modules/main/person.class.php **/

require_once ("classes/dbobject.class.php");

class CPerson extends CDBObject {
    var $person_id;
    var $person_name;
    var $address_id;

    var $_internal; // Not a DB field

    function __construct() { parent::__construct("persons", "person_id", array("person_id", "address_id")); }

    function getDefinition() {
        $types = array("person_id" => "int(11)", "person_name" => "varchar(255)", "address_id" => "int(11)");
        $default = null;
        $collation = null; // not implemented yet
        $attributes = null;
        $index = null; // not implemented yet
        return array($types, $default, $collation, $attributes, $index);
    }

    function set_triggers() { $this->add_trigger(new CRelatedObjectTrigger("CAddress", "address_id", "address")); }

    function upgrade($curr_version, $where = "WHERE 1") {
        switch ($curr_version) {
        case 0.1:
            $this->alter("ADD", "address_id int(11)");
        }
    }
}

We are modifying existing object. See upgrading for more info.

<?php
/** @file /application/modules/main/cron/upgrade_person.php **/

global $app;

$version = get_argv_value('version', 'v');

require_once ($app->getModuleFile("person.class.php"));

$person = new CPerson();
if ($version !== null) {
    $person->upgrade($version);
} else {
    $app->console_output(INFO, "missing version");
}
user@server:~/www/yoursite_domain$ ./cron.php -f upgrade_person --version 0
creating Database connector..
DB conecion stablished
select database `mysite_domain`
Setrill Framework v0.107 shell call
modifying table for CPerson

This is an example using the relationship

<?php

/** Relationships example **/

$person_a = new CPerson();
$person_b = new CPerson();
$persons = new CPerson();

$address = $person_a->load_by("person_name", "Carles")->insert_address();
if ($address->valid()) {
    $address->set_value("address_name", "Lleida")->update();
}


$address = $person_a->load_by("person_name", "Jaume")->insert_address();
if ($address->valid()) {
    $address->set_value("address_name", "Catalonia")->update();
    $person_b->load_by("person_name", "Marc")->assign_address($address);
}

foreach ($persons->get_from_address($address) as $person) {
    $app->console_output(INFO,
                         "person name in ".$address->get_value("address_name")." is ".$person->get_value("person_name"));
}

$person_a->print_data();
$person_b->print_data();
$address->print_data();

Most of magical's comes from using the CRelatedObjectTrigger. Note that we are using a string as object in it's constructor. This is possible because we have stored in below class definition. We could use an object itself but in this way we avoid problems when same object is constructed while contructing and also we have will not be creating until it's necessary, so it will not be created if not used.

The Output for this example is

INFO: person name in Catalonia is jaume
INFO: person name in Catalonia is Marc
==CPerson==
INTERNAL FLAGS: 100000 (100000000000000000000)
person_id: 1
person_name: jaume
address_id: 2
==CPerson==
INTERNAL FLAGS: 100000 (100000000000000000000)
person_id: 2
person_name: Marc
address_id: 2
==CAddress==
INTERNAL FLAGS: 100000 (100000000000000000000)
address_id: 2
address_name: Catalonia

Generated mysql are

INSERT INTO addresses () VALUES ();
UPDATE persons SET  `person_name` = 'Carles' , `address_id` = '1'  WHERE `person_id` = '1';
UPDATE addresses SET  `address_name` = 'Lleida'  WHERE `address_id` = '1';
INSERT INTO addresses () VALUES ();
UPDATE persons SET  `person_name` = 'Jaume' , `address_id` = '1'  WHERE `person_id` = '1';
UPDATE addresses SET  `address_name` = 'Catalonia'  WHERE `address_id` = '2';
SELECT * FROM persons WHERE `person_name` = 'Marc';
UPDATE persons SET  `person_name` = 'Marc' , `address_id` = '2'  WHERE `person_id` = '2';
SELECT * FROM persons WHERE `address_id` = '2';

To insert a relationship from a client, see ALTERNATIVE_ACTION_ADD

Quering Relationships

We can build queries to match relationships on objects. This is done using the CExtendedObject. Best practice is to extend a class from it.

<?php

class CPersonAddress extends CExtendedObject {
    function __construct() {
        CExtendedObject::__construct(new CPerson, "person");
        $this->addObj(new CAddress, "address", array("address_id" => "address_id"));
    }
}

This object is not installed, and it's used in the same way as a CDBObject

<?php

$personAddresses = new CPersonAddress();

foreach ($personAddresses->loadAll("*", CDBObject::data_query_field("address_name", "Catalonia")) as $personAddress) {
    if ($personAddress->person_name == "jaume") {
        $app->console_output(INFO, "Jaume is here!");
        $personAddress->print_data();
    }
    $app->console_output(INFO,
                         "Name: ".$personAddress->get_value("person_name")."; Address: ".$personAddress->address_name);
}

Once again, you can access data directly, but it's safer to use get_value, especially when it's an output to user.

INFO: Jaume is here!
==CPersonAddress==
INTERNAL FLAGS: 500008 (10100000000000000001000)
==>==CPerson==
INTERNAL FLAGS: 100001 (100000000000000000001)
person_id: 1
person_name: jaume
address_id: 2
NAMED as person
==>==CAddress==
INTERNAL FLAGS: 102011 (100000010000000010001)
address_id: 2
address_name: Catalonia
NAMED as address
[END]
INFO: Name: jaume; Address: Catalonia
INFO: Name: Marc; Address: Catalonia

flags for extended object are

  • (1 << 3) OBJECT_EXTENDED_OBJECT
  • (1 << 20) DB_OBJECT_FLAG_2 - EMPTY_SEARCH_ALLOWED
  • (1 << 22) EXTENDED_OBJECT_EXTEND_SEARCH_FROM_STRING

flags for main object (person) is

  • (1 << 0) OBJECT_ON_EXTENDED_OBJECT
  • (1 << 20) DB_OBJECT_FLAG_2 EMPTY_SEARCH_ALLOWED

flags for extending object (address) is

  • (1 << 0) OBJECT_ON_EXTENDED_OBJECT
  • (1 << 4) OBJECT_DISABLE_SEARCH_STRING
  • (1 << 13) OBJECT_DISABLE_KEY_SEARCH
  • (1 << 20) DB_OBJECT_FLAG_2 - EMPTY_SEARCH_ALLOWED

they are set to get expected query results. Changing the ones related to query is possible, and may affect results. They are set to restrict standard search procedures to main object.

The example generates only one sql query.

 SELECT person.person_id,person.person_name,person.address_id,address.address_name FROM persons as person 
INNER  JOIN  addresses as address ON address.address_id =  person.address_id 
WHERE `address_name` = 'Catalonia' GROUP BY person.person_id;

Extending object on demand

Objects can be extended on demand. This is usefull where are intended to be used only for query, so only nedded objects and related mysql stuff is added resulting in faster executions and less resources needed.

We need to modify our ExtendedObject

<?php

class CPersonAddress extends CExtendedObject {

    function __construct() { CExtendedObject::__construct(new CPerson, "person"); }

    function expand_object_on_query($k, $v, $op) {
        $ret = null;
        switch ($k) {
        case 'address_name':
            $this->addObj(new CAddress, "address", array("address_id" => "address_id"));
            break;
        default:
            $ret = parent::expand_object_on_query($k, $v, $op);
        }
        return $ret;
    }
}

The query must be performed in a higher level, wich is prefered to get code more cleaner.

<?php

$personAddresses = new CPersonAddress();

foreach ($app->getListObjects($personAddresses, array("person_name" => "Marc")) as $personAddress) {
    // Do something
}

foreach ($app->getListObjects($personAddresses, array("address_name" => "Lleida")) as $personAddress) {
    // Do something
}

The queries are done as expected. We could also use previous functions, adding the object manually as necessary

SELECT person.person_id,person.person_name,person.address_id FROM persons as person 
WHERE (`person`.`person_name` LIKE '%Marc%') OR (`person`.`person_id` is NULL) GROUP BY person.person_id;

SELECT person.person_id,person.person_name,person.address_id,address.address_name FROM persons as person 
INNER  JOIN  addresses as address ON address.address_id =  person.address_id 
WHERE ((1 = '1') OR (`person`.`person_id` is NULL)) AND (`address`.`address_name` LIKE '%Lleida%') 
GROUP BY person.person_id;

Notice the % added on query. See Listing objects for more info.

Multiple Databases

We may need to work with more than one database, eg, when importing or converting.

We need to create a link to the database. until now this has been transparent.

Same structure

this case, we can build a new object extending the one we already have.

Diferent or new structurecan build a object exteding CDBExternObject

Restoring data

This code snippet pertains to a custom application, and shows how to restore partial data from a database backup, for a especified period.

The file is intended to be placed on /cron folder of the module and to be executed from console.

 <?php

/** Restores journals from a backup table on certain period **/

class CAccountJournalBackUp extends CAccountJournal {
    function set_table($table) { return parent::set_table($table."_2020_04_29"); }
}

class CAccountJournalCurr extends CAccountJournal {
    function set_triggers() {
        parent::set_triggers();
        $this->allow_load_deleted();
    }

    function insert($omit_null = true) { return CDBObject::insert($omit_null); }

    function update($where = null, $order = "") { return CDBObject::update($where, $order); }

    function delete($where = null) { return CDBOBject::delete($where); }

    function delete_trigger_enable_real() { return true; }

    function on_valid_period($check_date = null) { return true; }
}

$curr = new CAccountJournalCurr();
$bak = new CAccountJournalBackup();

if ($app->read_optional("Restore al journals from 2019 from 2020_04_29?", "No")) {
    $app->console_output(INFO, "Deleting current data");
    $curr->delete($curr->between_start_end_query(strtotime("01-01-2019"), strtotime("31-12-2019")));
    if (!$curr->has_error()) {
        $app->console_output(INFO, "Creating new data");
        $curr->removeTriggers();
        foreach ($bak->load_all_between_start_end(strtotime("01-01-2019"), strtotime("31-12-2019")) as $ac_back) {
            if (!$curr->loadFromKey($ac_back->get_key_value())->valid()) {
                $curr->copy($ac_back)->insert();
                if ($curr->has_error()) {
                    $app->console_output(ERROR, "cannot insert, Exiting", $curr);
                    break;
                }
                $app->console_output(STEP);
            } else if ($curr->is_deleted()) {
                // The object is not present currently in period, but deleted: Replace
                $curr->copy($ac_back)->update();
                $app->console_output(STEP_ASTERISK);
            } else {
                $curr->copy($ac_back)->reset_id()->insert();
                $app->console_output(WARNING, "Object {$ac_back->str_id()} exists, inserted with {$curr->str_id()}");
            }
        }
        $curr->optimizeTable();
    } else {
        $app->console_output(ERROR, "error on delete", $curr);
    }
    $app->console_output(INFO, "End!");
} else {
    $app->console_output(INFO, "Canceled");
}

The object managing the data we want to restore, implements some triggers. Related to this code are

  • CDeleteTrigger
  • CPeriodTrigger

We need to create two classes extending the object, one for loading the backup data, and another one to not get interfered with triggers and stuff added on the object. We do not remove the triggers at class level, because we use functions from them.

The overrided on_valid_period function is defined on the object itself.

Html Content

On Server Side

Although output of file is directly set as response, there is a module to get things properly shown, allowing to create content in a more structured dinamic way

generate content using jade template file.

We need the Layout Module and Jade Section

This code allows to generate html content on server from a .jade file.

<?php
/** Generate html content from a jade template **/

global $app;

$current->addJadeFile($app->getModuleFile("template.jade"), array("-p" => dirname(__FILE__)."/."));

The code must be placed in a file added to layout using CLayoutZone::addFile function

current is the CLayoutZone object where the content is added.

the -p parameter is optional, allows using properly import on template.

Get content on data call

Generated html content can also be retrieved using a data call

 
http://www.yoursite.domain/data/layout/html_content.xml?file=file&module=module§ion=section

Parameters are filled on CApp:getModuleFile function

  • file

    file name without extension. If not set gets the module or section name as file.

  • module

    module name, optional. defaults to main

  • section

    section name, optional. Set if necessary

The result of call is

 <?xml version="1.0" encoding="utf-8" ?>
<CONTENT><![CDATA[ html ]]></CONTENT>

html is the html content

On Client Side (Javascript)

Server - Client development

Listing objects

Module Maintenance

Deployment

You can create a deploy file for a module. The information is get from module.def.php, and this is a way to keep things in order, have clear files and things installed, and allow clean unnistalls.

user@server:~/www/yoursite_domain$ ./cron.php -m setrill -f get_deploy_file --module setrill
Setrill Framework v0.107 shell call
file created: cache/deploy/setrill_0_107.zip

The file you get contains a file named module.xml with information of the deploied module, and a deploy folder based on root folder of setrill containing the file all files that will be copied.

Create The module definition file

Installing

Upgrading