ED php framework :: Tutorial #14
Desktop application(s)
A live example can be checked here: desktop application

First you have to define in 2 xml files the start menu and the desktop icons.
snippet/desktop/
menu.xml
<tree>
    <node text="Customers" icon="customers_32x32.png">
        <node text="Show all customers" icon="customers_32x32.png" url="/desktop/customers/list.php" unique="true" />
    </node>
 
    <node text="Tools" icon="tools_32x32.png">
        <node text="Setup database" icon="database_32x32.png" />
        <node text="Monitor services" icon="monitor_32x32.png" />
        <node text="Monitor performance" icon="performance_32x32.png" />
    </node>
 
    <node text="Reports" icon="reports_32x32.png">
        <node text="Customers activity" icon="report_32x32.png" />
        <node text="Sales" icon="report_32x32.png" />
        <node text="Site visits" icon="report_32x32.png" url="/desktop/reports/site_visits.php" title="Site visits" />
    </node>
 
    <node text="Video Tutorials" icon="video_32x32.png">
        <node text="What is PHP" icon="video_32x32.png" url="/desktop/media/youtube.php?code=fT7rCWZvsD8" title="What is PHP" />
        <node text="Object Oriented Programming" icon="video_32x32.png" url="/desktop/media/youtube.php?code=c5kfCH50wl0" title="Object Oriented Programming" />
    </node>
 
    <separator />
    <node text="Google Map" icon="map_32x32.png" url="/desktop/media/google_map.html" dimension="800x500" title="Google Map" resizable="true" />
 
    <separator />
    <node text="Configuration" icon="config_32x32.png" url1="/config.php" />
 
    <separator />
    <node text="Exit" icon="exit_32x32.png" url="http://edphp.net" redirect="true" confirm="true" message="Do you want to leave ?" />
 
</tree>
 
snippet/desktop/
desktop.xml
<desktop>
    <icon text="Customers" icon="customers_48x48a.png" url="/desktop/customers/list.php" unique="true" />
    <icon text="Customers (Page-based)" icon="customers_48x48b.png" url="/desktop/customers/list.php?mode=pages" unique="true" />
    <icon text="What is PHP" icon="video_32x32.png" url="/desktop/media/youtube.php?code=fT7rCWZvsD8" title="What is PHP" />
    <icon text="Object Oriented Programming" icon="video_32x32.png" url="/desktop/media/youtube.php?code=c5kfCH50wl0" title="Object Oriented Programming" />
    <icon text="Google Map" icon="map_32x32.png" url="/desktop/media/google_map.html" dimension="800x500" title="Google Map" resizable="true" />
    <icon text="Exit" icon="exit_32x32.png" url="http://edphp.net" redirect="true" confirm="true" message="Do you want to leave ?" />
</desktop>
 


Then you create the desktop by extending the TDesktop class.
snippet/desktop/
index.php
<?php include "index.php.script"; $page = TPage::create(); ?>
snippet/desktop/
index.php.script
<?php
 
   include_once("../../../appconfig.inc");
   registerWidget("desktop");
 
 
   /**
    * IndexPage
    */
   class IndexPage extends TDesktop {
 
       /**
        * Constructor
        */
       public function IndexPage() {
           parent::TDesktop();
       }
 
 
       /**
        * On page load event
        */
       public function OnPageLoad() {
           $this->setTitle("Desktop application");
 
           $this->setTheme("dark");
           $this->setDefaultBackgroundImage();
 
           $this->addBackgroundImage("/desktop/img/sleeping.gif", Array(
                          "position"  =>  "right bottom",
                          "shiftX"    =>  -10,
                          "shiftY"    =>  -40
           ));
 
           $this->setIconDirectory("/assets/images/desktop/");
           $this->loadIcons(PROTECTED_XML_DIR . "desktop.xml");
           $this->startMenu->loadFromXmlFile(PROTECTED_XML_DIR . "menu.xml");
 
           parent::OnPageLoad();
       }
 
    }
?>


Afterthat for creating pages/windows all your PHP classes must extend TWindow. There are 2 special classes to query/insert/view/edit data. These classes are: TWindow_Grid and TWindow_FieldList. They cover all basic operstions an admin module might need. Here comes a sample:
snippet/desktop/
list.php.script
<?php
 
   include_once("../../../../appconfig.inc");
 
   registerWidget("desktop");
   registerWidget("date_and_time");
   registerWidget("input");
 
 
 
   /**
    * TPersonList
    */
   class TPersonList extends TWindow_Grid {
 
       public $ftPersonId;
       public $ftFirstName;
       public $ftLastName;
       public $ftBornAfter;
       public $ftBornBefore;
       public $ftSalaryFrom;
       public $ftSalaryTo;
       public $ftLastUpdatedAfter;
       public $ftLastUpdatedBefore;
       public $ftCountry;
       public $ftCountrySelectionList;
       public $ftBoolItems;
 
 
 
       /**
        * Constructor
        */
       public function TPersonList() {
           parent::TWindow_Grid();
 
           $this->ftPersonId = new TTextBox();
           $this->ftFirstName = new TTextBox();
           $this->ftLastName = new TTextBox();
           $this->ftBornAfter = new TDateField();
           $this->ftBornBefore = new TDateField();
           $this->ftSalaryFrom = new TTextBox();
           $this->ftSalaryTo = new TTextBox();
           $this->ftCountry = new TSelectBox();
           $this->ftCountrySelectionList = new TLabel();
           $this->ftLastUpdatedAfter = new TDateTimeField();
           $this->ftLastUpdatedBefore = new TDateTimeField();
           $this->ftBoolItems = new TCheckItemGroup();
 
           /**
            * Set properties
            */
           $this->ftSalaryFrom->setProperty("Style", "width:60px");
           $this->ftSalaryTo->setProperty("Style", "width:60px");
 
           $this->ftBornAfter->setProperty("StartYear", 1900);
           $this->ftBornBefore->setProperty("StartYear", 1900);
 
           $this->ftCountry->style->width = "200px";
           $this->ftCountry->setProperty("Size", 10);
           $this->ftCountry->setProperty("Multiple", TRUE);
 
           $this->ftCountrySelectionList->setProperty("TagName", "div");
 
           $this->ftBoolItems->setProperty("ReverseOrder", TRUE);
           $this->ftBoolItems->setProperty("StyleIcon", "float:right");
           $this->ftBoolItems->defineItems(Array(
                     "single"        => "Single"
           ));
       }
 
 
 
       /**
        * On create event
        */
       public function OnCreate() {
           parent::OnCreate();
 
           $this->includeScript("list.js");
 
           if ($this->request->param("mode") == "pages") {
               $this->grid->setPerPage(10);
               $this->setDimension('auto', 'auto');
           }
           else {
               $this->grid->setPerPage(1000000);
               $this->setDimension('auto', 400);
               $this->setResizable(TRUE);
           }
 
           $this->setTitle("Customer list");
 
           $this->search->addField("Person Id", $this->ftPersonId);
           $this->search->addField("First name", $this->ftFirstName);
           $this->search->addField("Last name", $this->ftLastName);
           $this->search->addField("Salary", Array("From ", $this->ftSalaryFrom, " to ", $this->ftSalaryTo));
 
           $this->search->addNewColumn();
           $this->search->addField("Born after", $this->ftBornAfter);
           $this->search->addField("Born before", $this->ftBornBefore);
           $this->search->addField("Last updated after", $this->ftLastUpdatedAfter);
           $this->search->addField("Last updated before", $this->ftLastUpdatedBefore);
 
           $this->search->addNewColumn();
           $this->search->addField("Country", Array($this->ftCountry, 
                                                    $this->ftCountrySelectionList));
           $this->search->addField("Yes/No flags", $this->ftBoolItems);
 
           $this->showSearchButton();
           $this->showRefreshButton();
 
           $this->footer->addButton(Array(
                              "id"     =>   "btnNewCustomer",
                              "icon"   =>   "img/new_customer.png",
                              "text"   =>   "New Customer"
                            ));
 
           $db = db();
 
           /**
            * Load country list
            */
           $dataSource = $db->select(TSqlStatement::sql("select * from countries order by name"));
           $this->ftCountry->loadFromDataSource($dataSource, "country_id", "name");
 
           $this->grid->load();
       }
 
 
       /**
        * On data grid event
        */
       public function grid_OnEvent() {
 
           parent::grid_OnEvent();
 
           $sql = TSqlStatement::sql(PERSON_SQL_FILE, "SQL_PERSONS_LIST_CRITERIA");
 
           if ($this->isSearchMode()) {
               if ($this->ftPersonId->getValue()) {
                   TDataValidator::checkInt($this->ftPersonId->getValue(),
                            "Person Id should be a number");
               }
 
               if ($this->ftSalaryFrom->getValue()) {
                   TDataValidator::checkMoney($this->ftSalaryFrom->getValue(),
                            "'Salary from' must have money format like 10.00");
               }
 
               if ($this->ftSalaryTo->getValue()) {
                   TDataValidator::checkMoney($this->ftSalaryTo->getValue(),
                            "'Salary to' must have money format like 10.00");
               }
 
               $fieldPlayerId      = TSqlCriteria::field('p.person_id', 'number')
                                           ->equals($this->ftPersonId->getValue());
 
               $fieldFirstName     = TSqlCriteria::field('p.person_fname', 'string')
                                           ->contains($this->ftFirstName->getValue());
 
               $fieldLastName      = TSqlCriteria::field('p.person_lname', 'string')
                                           ->contains($this->ftLastName->getValue());
 
               $fieldCountry       = TSqlCriteria::field('p.country_id', 'array')
                                           ->in(array_keys($this->ftCountry->selectedItems()));
 
               $fieldSalaryFrom    = TSqlCriteria::field('p.salary', 'number')
                                           ->greaterEqual($this->ftSalaryFrom->getValue());
 
               $fieldSalaryTo      = TSqlCriteria::field('p.salary', 'number')
                                           ->lessEqual($this->ftSalaryTo->getValue());
 
               $fieldBornAfter     = TSqlCriteria::field('p.birthday', 'date')
                                           ->greaterEqual($this->ftBornAfter->getValue());
 
               $fieldBornBefore    = TSqlCriteria::field('p.birthday', 'date')
                                           ->lessEqual($this->ftBornBefore->getValue());
 
               $fieldLUAfter       = TSqlCriteria::field('p.last_updated', 'timestamp')
                                           ->greaterEqual($this->ftLastUpdatedAfter->getValue());
 
               $fieldLUBefore      = TSqlCriteria::field('p.last_updated', 'timestamp')
                                           ->lessEqual($this->ftLastUpdatedBefore->getValue());
 
               $fieldSingle        = TSqlCriteria::field('p.single', 'bool')
                                           ->equals($this->ftBoolItems->getItemValue("single"));
 
 
 
               $group = TSqlCriteria::group('and');
               $group->add($fieldPlayerId);
               $group->add($fieldFirstName);
               $group->add($fieldLastName);
               $group->add($fieldCountry);
               $group->add($fieldSalaryFrom)->add($fieldSalaryTo);
               $group->add($fieldBornAfter)->add($fieldBornBefore);
               $group->add($fieldLUAfter)->add($fieldLUBefore);
               $group->add($fieldSingle);
 
               $criteria = TSqlCriteria::create();
               $criteria->setFirstGroup($group);
 
               $strCriteria = $criteria->toSql();
               $sql->replace("[criteria]", $strCriteria ? "and " . $strCriteria : "");
           }
           else {
               $sql->replace("[criteria]", "");
           }
 
           $this->grid->bindSQL($sql, "[orderString]");
        }
 
 
        /**
         * On cancel search
         */
        protected function OnCancelSearch() {
            parent::OnCancelSearch();
            $this->ftCountrySelectionList->setText("");
        }
 
 
        /**
         * On button click
         */
        public function btnRefresh_OnClick() {
            $this->grid->load();
        }
 
 
        /**
         * On button click
         */
        public function btnNewCustomer_OnClick() {
            $this->openWindow("/desktop/customers/new.php", Array(
                              "unique" => TRUE
            ));
        }
 
 
        /**
         * On button click
         */
        public function viewCustomer() {
            $id = $this->request->param("id");
            $this->openWindow("/desktop/customers/item.php?id={$id}", Array(
                              "unique" => TRUE
            ));
        }
 
 
        /**
         * On button click
         */
        public function deleteCustomer() {
            $id = $this->request->param("id");
            $this->addError("Customer {$id} can't be deleted for security reasons");
        }
 
 
        /**
         * Page exception event
         *
         * @param Exception $ex exception
         */
        public function OnPageException($ex) {
            if (is_class($ex, 'TDataValidatorException')) {
                $this->grid->setDataSource(NULL);
            }
 
            parent::OnPageException($ex);
        }
 
    }
?>
snippet/desktop/
view.php.script
<?php
 
   include_once("../../../../appconfig.inc");
 
   registerWidget("desktop");
   registerWidget("date_and_time");
   registerWidget("input");
   registerWidget("file_upload");
 
 
   /**
    * TCustomer_View
    */
   class TCustomer_View extends TWindow_FieldList {
 
       public $dfFirstName;
       public $dfLastName;
       public $dfCountry;
       public $dfBirthdate;
       public $dfSingle;
       public $dfSalary;
       public $dfPhoto;
       public $vPhoto;
       public $dfLastUpdated;
 
 
       /**
        * Constructor
        */
       public function TCustomer_View() {
           parent::TWindow_FieldList();
 
           $this->dfFirstName = new TTextBox();
           $this->dfLastName = new TTextBox();
           $this->dfCountry = new TSelectBox();
           $this->dfBirthdate = new TDateField();
           $this->dfSingle = new TCheckItem();
           $this->dfSalary = new TMoneyBox();
           $this->dfPhoto = new TFileUpload();
           $this->vPhoto = new TImage();
           $this->dfLastUpdated = new TDateTimeField();
 
           $this->dfBirthdate->setProperty("StartYear", 1900);
           $this->dfPhoto->setProperties(Array(
                    "Multi"         =>   FALSE,
                    "MaxFileSize"   =>   512*1024,
                    "Filters"       =>   Array(
                          "*.jpg;*.jpeg;*.gif;*.png" => "Images(*.jpg, *.jpeg, *.gif, *.png)"
                     )
           ));
 
           $this->vPhoto->setCropArea(250, 220, "border:1px dashed #CCCCCC;");
           $this->vPhoto->setProperty("EnlargeHandler", "handleViewPhoto");
 
           $this->enableUpload();
       }
 
 
       /**
        * On create event
        */
       public function OnCreate() {
           parent::OnCreate();
 
           $id = request()->getParam("id");
           $this->model->setProperty("id", $id, TRUE, TRUE);
 
           /**
            * Load customer
            */
           $sql = TSqlStatement::sql(PERSON_SQL_FILE, "SQL_PERSON_ENTRY");
           $sql->bindInteger(":id", $id);
           $dataSource = db()->select($sql);
 
           if ($dataSource->isEmpty()) {
               return $this->addError("No record was found.");
           }
 
           $record = $dataSource->record(0);
 
           $photoStream = db()->blob($record["photo"]);
           $record["photo"] = NULL;
 
           $this->dfCountry->loadFromDataSource(db()->select(TSqlStatement::sql("select * from countries order by name")), "country_id", "name");
 
           $this->setTitle("View customer :: " . $record["person_id"]);
 
           $this->vPhoto->setBinaryData($photoStream);
           $this->dfFirstName->setValue($record["person_fname"]);
           $this->dfLastName->setValue($record["person_lname"]);
           $this->dfBirthdate->setValue($record["birthday"]);
           $this->dfCountry->setValue($record["country_id"]);
           $this->dfSalary->setValue($record["salary"]);
           $this->dfSingle->setValue(isSqlTrue($record["single"]) ? TRUE : FALSE);
           $this->dfLastUpdated->setValue($record["last_updated"]);
 
 
           $this->fieldList->addView("Customer Id", $record["person_id"]);
           $this->fieldList->addViewAndEdit("Photo", $this->vPhoto, $this->dfPhoto);
 
           $this->fieldList->addNewColumn();
           $this->fieldList->addViewAndEdit("First name", $record["person_fname"], $this->dfFirstName);
           $this->fieldList->addViewAndEdit("Last name", $record["person_lname"], $this->dfLastName);
           $this->fieldList->addViewAndEdit("Birthday", $this->dfBirthdate->getText(), $this->dfBirthdate);
 
           $this->fieldList->addNewColumn();
           $this->fieldList->addViewAndEdit("Country", $record["country_name"], $this->dfCountry);
 
           $this->fieldList->addNewColumn();
           $this->fieldList->addViewAndEdit("Single", isSqlTrue($record["single"]) ? "Yes" : "No", $this->dfSingle);
           $this->fieldList->addViewAndEdit("Salary", "$" . $record["salary"], $this->dfSalary);
           $this->fieldList->addViewAndEdit("Last updated", $this->dfLastUpdated->getText(), $this->dfLastUpdated);
 
           $this->fieldList->setEditFields(TRUE);
 
           $this->setHeight(400);
           $this->setMaxVisibleColumns(2);
           $this->setResizable(TRUE);
 
           $this->footer->addButton(Array(
                              "id"     =>   "btnDone",
                              "icon"   =>   "img/done.png",
                              "text"   =>   "Done"
                            ));
       }
 
 
       /**
        * Handle view photo
        */
       public function handleViewPhoto() {
           $id = $this->model->getProperty("id");
           $db = db();
 
           $record = $db->getTableRecord("persons", "person_id", $id);
           if ($record == NULL) {
               return $this->showError("No record was found.");
           }
 
           if ($record['photo']) {
               $this->showImageBinaryData($db->blob($record['photo']));
           }
       }
 
 
       /**
        * On field submit
        */
       public function dfPhoto_OnSubmit() {
           TDataValidator::checkIntRange($this->dfPhoto->size(), 1, 1, "File is not selected");
           $file = $this->dfPhoto->getFile(0);
 
           $dim = TImage::getDimensionFromBinaryData($file->getBinaryData());
           if ($dim['width'] > 700 || $dim['height'] > 500) {
               $this->dfPhoto->deleteTemporaryFiles();
               return $this->showError(sprintf("There are image limits - width less than %dpx and height less than %dpx.<br/><br/>Your image dimension is %dw x %dh", 700, 500, $dim['width'], $dim['height']));
           }
 
           $binarySmall = TImage::createThumbnail($file->getBinaryData(), 32, 32);
 
           $db = db();
 
           $dataParams = TSqlParams::create();
           $dataParams->set_blob("photo", $file->getBinaryData());
           $dataParams->set_blob("photo_small", $binarySmall);
 
           $filterParams = TSqlParams::create();
           $filterParams->set_integer("person_id", $this->model->getProperty("id"));
 
           $db->update("persons", $dataParams, $filterParams);
 
           $this->vPhoto->setBinaryData($file->getBinaryData());
           $this->dfPhoto->deleteTemporaryFiles();
 
           $this->fieldList->showView();
       }
 
 
       /**
        * On field submit
        */
       public function dfFirstName_OnSubmit() {
           $value = $this->dfFirstName->getValue();
           TDataValidator::checkEmpty($value, "First name is empty");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_string("person_fname", $value);
           $this->saveItemProperty($dataParams, $value);
       }
 
 
       /**
        * On field submit
        */
       public function dfLastName_OnSubmit() {
           $value = $this->dfLastName->getValue();
           TDataValidator::checkEmpty($value, "Last name is empty");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_string("person_lname", $value);
           $this->saveItemProperty($dataParams, $value);
       }
 
 
       /**
        * On field submit
        */
       public function dfBirthdate_OnSubmit() {
           $value = $this->dfBirthdate->getValue();
           TDataValidator::checkDate($value, "Birthday must be in date format");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_date("birthday", $value);
           $this->saveItemProperty($dataParams, $this->dfBirthdate->getText());
       }
 
 
       /**
        * On field submit
        */
       public function dfCountry_OnSubmit() {
           $value = $this->dfCountry->getValue();
           TDataValidator::checkEmpty($value, "Country is not selected");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_string("country_id", $value);
           $this->saveItemProperty($dataParams, $this->dfCountry->getText());
       }
 
 
       /**
        * On field submit
        */
       public function dfSingle_OnSubmit() {
           $value = $this->dfSingle->getValue();
           TDataValidator::checkNull($value, "Single must be checked or unchecked");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_bool("single", $value);
           $this->saveItemProperty($dataParams, $value === TRUE ? "Yes" : "No");
       }
 
 
       /**
        * On field submit
        */
       public function dfSalary_OnSubmit() {
           $value = $this->dfSalary->getValue();
           TDataValidator::checkMoney($value, "Salary must have money format like 10.00");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_number("salary", $value);
           $this->saveItemProperty($dataParams, "$" . $value);
       }
 
 
       /**
        * On field submit
        */
       public function dfLastUpdated_OnSubmit() {
           $value = $this->dfLastUpdated->getValue();
           TDataValidator::checkDateTime($value, "Last updated must be in date/time format");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_date("last_updated", $value);
           $this->saveItemProperty($dataParams, $this->dfLastUpdated->getText());
       }
 
 
       /**
        * Save item property
        */
       private function saveItemProperty($dataParams, $value) {
           $filterParams = TSqlParams::create();
           $filterParams->set_integer("person_id", $this->model->getProperty("id"));
 
           db()->update("persons", $dataParams, $filterParams);
 
           $this->fieldList->updateView($value);
       }
 
 
       /**
        * On button click
        */
       public function btnDone_OnClick() {
           $this->close();
       }
 
    }
?>
snippet/desktop/
new.php.script
<?php
 
   include_once("../../../../appconfig.inc");
 
   registerWidget("desktop");
   registerWidget("date_and_time");
   registerWidget("file_upload");
   registerWidget("input");
 
 
   /**
    * TCustomer_New
    */
   class TCustomer_New extends TWindow_FieldList {
 
       public $dfFirstName;
       public $dfLastName;
       public $dfCountry;
       public $dfBirthdate;
       public $dfSingle;
       public $dfSalary;
       public $dfPhoto;
 
 
 
       /**
        * Constructor
        */
       public function TCustomer_New() {
           parent::TWindow_FieldList();
 
           $this->dfFirstName = new TTextBox();
           $this->dfLastName = new TTextBox();
           $this->dfCountry = new TSelectBox();
           $this->dfBirthdate = new TDateField();
           $this->dfSingle = new TCheckItem();
           $this->dfSalary = new TMoneyBox();
           $this->dfPhoto = new TFileUpload();
 
           $this->dfBirthdate->setProperty("StartYear", 1900);
           $this->dfPhoto->setProperties(Array(
                    "Multi"         =>   FALSE,
                    "MaxFileSize"   =>   512*1024,
                    "Filters"       =>   Array(
                          "*.jpg;*.jpeg;*.gif;*.png" => "Images(*.jpg, *.jpeg, *.gif, *.png)"
                     )
           ));
 
           $this->enableUpload();
       }
 
 
       /**
        * On create event
        */
       public function OnCreate() {
           parent::OnCreate();
 
           $this->dfCountry->loadFromDataSource(db()->select(TSqlStatement::sql("select * from countries order by name")), "country_id", "name");
 
           $this->setTitle("New customer");
 
           $this->fieldList->addField("First name", $this->dfFirstName);
           $this->fieldList->addField("Last name", $this->dfLastName);
           $this->fieldList->addField("Single", $this->dfSingle);
 
           $this->fieldList->addNewColumn();
           $this->fieldList->addField("Country", $this->dfCountry);
           $this->fieldList->addField("Birthday", $this->dfBirthdate);
 
           $this->fieldList->addNewColumn();
           $this->fieldList->addField("Salary", $this->dfSalary);
           $this->fieldList->addField("Photo", $this->dfPhoto);
 
           $this->footer->addButton(Array(
                              "id"     =>   "btnSave",
                              "icon"   =>   "img/save.png",
                              "text"   =>   "Save"
                            ));
 
           $this->footer->addButton(Array(
                              "id"     =>   "btnClose",
                              "icon"   =>   "img/close.png",
                              "text"   =>   "Close"
                            ));
 
           $this->setHeight(400);
           $this->setMaxVisibleColumns(2);
           $this->setResizable(TRUE);
       }
 
 
       /**
        * On button click
        */
       public function btnSave_OnClick() {
           TDataValidator::checkEmpty($this->dfFirstName->getValue(), "First name is empty");
           TDataValidator::checkEmpty($this->dfLastName->getValue(), "Last name is empty");
           TDataValidator::checkNull($this->dfSingle->getValue(), "Single is not selected");
           TDataValidator::checkDate($this->dfBirthdate->getValue(), "Birthday must be in date format");
           TDataValidator::checkEmpty($this->dfCountry->getValue(), "Country is empty");
           TDataValidator::checkMoney($this->dfSalary->getValue(), "Salary should have money format like 10.00");
 
           $db = db();
 
           $personID = $db->nextSequence("seq_default");
 
           $dataParams = TSqlParams::create();
           $dataParams->set_integer("person_id", $personID);
           $dataParams->set_string("person_fname", $this->dfFirstName->getValue());
           $dataParams->set_string("person_lname", $this->dfLastName->getValue());
           $dataParams->set_bool("single", $this->dfSingle->getValue());
           $dataParams->set_date("birthday", $this->dfBirthdate->getValue());
           $dataParams->set_string("country_id", $this->dfCountry->getValue());
           $dataParams->set_number("salary", $this->dfSalary->getValue());
           if ($this->dfPhoto->size() == 1) {
               $file = $this->dfPhoto->getFile(0);
               $dataParams->set_blob("photo", $file->getBinaryData());
           }
 
           db()->insert("persons", $dataParams);
 
           $this->showMessageAndClose("Customer has been created");
       }
 
 
       /**
        * On button click
        */
       public function btnClose_OnClick() {
           $this->close();
       }
 
    }
?>
With this package the time to create a very professional administration module can be reduced to a couple of days. Something which in the past could have taken many weeks.