From 5a43e7a2ea0cf7af35c100cb67e4a53566cbc496 Mon Sep 17 00:00:00 2001 From: tbrehm <t.brehm@ispconfig.org> Date: Fri, 03 Feb 2012 05:58:01 -0500 Subject: [PATCH] - Implemented new backup and restore functions for websites and databases (see also FS#1389) - Added "actions" framework in server to replace the functions provided by the core modules - Moved system update function from remoteactins core module to software update plugin. --- interface/web/sites/templates/database_edit.htm | 18 server/plugins-available/openvz_plugin.inc.php | 54 +++ interface/web/mail/mail_user_edit.php | 8 interface/web/sites/database_edit.php | 93 +--- install/tpl/server.ini.master | 2 server/plugins-available/software_update_plugin.inc.php | 34 + interface/web/admin/form/server_config.tform.php | 10 interface/web/admin/templates/server_config_server_edit.htm | 10 interface/web/sites/lib/lang/en_web_backup_list.lng | 16 server/plugins-available/mysql_clientdb_plugin.inc.php | 12 install/sql/ispconfig3.sql | 20 + server/lib/classes/modules.inc.php | 38 ++ server/mods-available/remoteaction_core_module.inc.php | 7 interface/lib/classes/plugin_backuplist.inc.php | 138 ++++++++ interface/web/sites/templates/web_domain_backup.htm | 2 interface/web/sites/form/database.tform.php | 11 interface/web/themes/default/css/screen/content_ispc.css | 2 interface/web/sites/form/web_domain.tform.php | 11 server/cron_daily.php | 162 +++++++- interface/web/js/scrigo.js.php | 6 server/plugins-available/backup_plugin.inc.php | 134 +++++++ interface/web/sites/templates/web_backup_list.htm | 40 ++ interface/web/sites/lib/lang/en_database.lng | 5 server/lib/classes/db_mysql.inc.php | 7 server/lib/classes/plugins.inc.php | 42 ++ interface/web/admin/lib/lang/en_server_config.lng | 5 interface/web/sites/web_domain_edit.php | 23 + interface/web/sites/tools.inc.php | 2 server/server.php | 33 - server/mods-available/web_module.inc.php | 11 install/sql/incremental/upd_0028.sql | 12 interface/web/sites/web_domain_del.php | 6 32 files changed, 806 insertions(+), 168 deletions(-) diff --git a/install/sql/incremental/upd_0028.sql b/install/sql/incremental/upd_0028.sql new file mode 100644 index 0000000..0020cdd --- /dev/null +++ b/install/sql/incremental/upd_0028.sql @@ -0,0 +1,12 @@ +ALTER TABLE `web_database` ADD `parent_domain_id` int(11) unsigned NOT NULL DEFAULT '0' AFTER `server_id`; +ALTER TABLE `web_database` ADD `backup_interval` VARCHAR( 255 ) NOT NULL DEFAULT 'none', ADD `backup_copies` INT NOT NULL DEFAULT '1' AFTER `remote_ips`; +CREATE TABLE `web_backup` ( + `backup_id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `parent_domain_id` int(10) unsigned NOT NULL, + `backup_type` enum('web','mysql') NOT NULL DEFAULT 'web', + `backup_mode` varchar(64) NOT NULL DEFAULT '', + `tstamp` int(10) unsigned NOT NULL, + `filename` varchar(255) NOT NULL, + PRIMARY KEY (`backup_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql index 9c2d5cf..fdd31a8 100644 --- a/install/sql/ispconfig3.sql +++ b/install/sql/ispconfig3.sql @@ -1438,6 +1438,23 @@ -- -------------------------------------------------------- -- +-- Table structure for table `web_backup` +-- + +CREATE TABLE `web_backup` ( + `backup_id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `parent_domain_id` int(10) unsigned NOT NULL, + `backup_type` enum('web','mysql') NOT NULL DEFAULT 'web', + `backup_mode` varchar(64) NOT NULL DEFAULT '', + `tstamp` int(10) unsigned NOT NULL, + `filename` varchar(255) NOT NULL, + PRIMARY KEY (`backup_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- -- Table structure for table `web_database` -- @@ -1449,6 +1466,7 @@ `sys_perm_group` varchar(5) DEFAULT NULL, `sys_perm_other` varchar(5) DEFAULT NULL, `server_id` int(11) unsigned NOT NULL DEFAULT '0', + `parent_domain_id` int(11) unsigned NOT NULL DEFAULT '0', `type` varchar(16) NOT NULL DEFAULT 'y', `database_name` varchar(64) DEFAULT NULL, `database_user` varchar(64) DEFAULT NULL, @@ -1456,6 +1474,8 @@ `database_charset` varchar(64) DEFAULT NULL, `remote_access` enum('n','y') NOT NULL DEFAULT 'y', `remote_ips` text NOT NULL, + `backup_interval` VARCHAR( 255 ) NOT NULL DEFAULT 'none', + `backup_copies` INT NOT NULL DEFAULT '1', `active` enum('n','y') NOT NULL DEFAULT 'y', PRIMARY KEY (`database_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; diff --git a/install/tpl/server.ini.master b/install/tpl/server.ini.master index 71e61e6..275fa7a 100644 --- a/install/tpl/server.ini.master +++ b/install/tpl/server.ini.master @@ -13,7 +13,7 @@ nameservers=192.168.0.1,192.168.0.2 loglevel=2 backup_dir=/var/backup -backup_dir_ftpread=n +backup_mode=rootgz [mail] module=postfix_mysql diff --git a/interface/lib/classes/plugin_backuplist.inc.php b/interface/lib/classes/plugin_backuplist.inc.php new file mode 100644 index 0000000..ac0396b --- /dev/null +++ b/interface/lib/classes/plugin_backuplist.inc.php @@ -0,0 +1,138 @@ +<?php + +/* +Copyright (c) 2012, Till Brehm, ISPConfig UG +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of ISPConfig nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +class plugin_backuplist extends plugin_base { + + var $module; + var $form; + var $tab; + var $record_id; + var $formdef; + var $options; + + function onShow() { + + global $app; + + $listTpl = new tpl; + $listTpl->newTemplate('templates/web_backup_list.htm'); + + //* Loading language file + $lng_file = "lib/lang/".$_SESSION["s"]["language"]."_web_backup_list.lng"; + include($lng_file); + $listTpl->setVar($wb); + + $message = ''; + $error = ''; + + if(isset($_GET['backup_action'])) { + $backup_id = intval($_GET['backup_id']); + + if($_GET['backup_action'] == 'download' && $backup_id > 0) { + $sql = "SELECT count(action_id) as number FROM sys_remoteaction WHERE action_state = 'pending' AND action_type = 'backup_download' AND action_param = '$backup_id'"; + $tmp = $app->db->queryOneRecord($sql); + if($tmp['number'] == 0) { + $message .= $wb['download_info_txt']; + $sql = "INSERT INTO sys_remoteaction (server_id, tstamp, action_type, action_param, action_state, response) " . + "VALUES (". + (int)$this->form->dataRecord['server_id'] . ", " . + time() . ", " . + "'backup_download', " . + "'".$backup_id."', " . + "'pending', " . + "''" . + ")"; + $app->db->query($sql); + } else { + $error .= $wb['download_pending_txt']; + } + } + if($_GET['backup_action'] == 'restore' && $backup_id > 0) { + $sql = "SELECT count(action_id) as number FROM sys_remoteaction WHERE action_state = 'pending' AND action_type = 'backup_restore' AND action_param = '$backup_id'"; + $tmp = $app->db->queryOneRecord($sql); + if($tmp['number'] == 0) { + $message .= $wb['restore_info_txt']; + $sql = "INSERT INTO sys_remoteaction (server_id, tstamp, action_type, action_param, action_state, response) " . + "VALUES (". + (int)$this->form->dataRecord['server_id'] . ", " . + time() . ", " . + "'backup_restore', " . + "'".$backup_id."', " . + "'pending', " . + "''" . + ")"; + $app->db->query($sql); + } else { + $error .= $wb['restore_pending_txt']; + } + } + + } + + //* Get the data + $sql = "SELECT * FROM web_backup WHERE parent_domain_id = ".$this->form->id." ORDER BY tstamp DESC, backup_type ASC"; + $records = $app->db->queryAllRecords($sql); + + $bgcolor = "#FFFFFF"; + if(is_array($records)) { + foreach($records as $rec) { + + // Change of color + $bgcolor = ($bgcolor == "#FFFFFF")?"#EEEEEE":"#FFFFFF"; + $rec["bgcolor"] = $bgcolor; + + $rec['date'] = date($app->lng('conf_format_datetime'),$rec['tstamp']); + $rec['backup_type'] = $wb[('backup_type_'.$rec['backup_type'])]; + + $records_new[] = $rec; + } + } + + $listTpl->setLoop('records',@$records_new); + + $listTpl->setVar('parent_id',$this->form->id); + $listTpl->setVar('msg',$message); + $listTpl->setVar('error',$error); + + // Setting Returnto information in the session + $list_name = 'backup_list'; + // $_SESSION["s"]["list"][$list_name]["parent_id"] = $app->tform_actions->id; + $_SESSION["s"]["list"][$list_name]["parent_id"] = $this->form->id; + $_SESSION["s"]["list"][$list_name]["parent_name"] = $app->tform->formDef["name"]; + $_SESSION["s"]["list"][$list_name]["parent_tab"] = $_SESSION["s"]["form"]["tab"]; + $_SESSION["s"]["list"][$list_name]["parent_script"] = $app->tform->formDef["action"]; + $_SESSION["s"]["form"]["return_to"] = $list_name; + + return $listTpl->grab(); + } + +} + +?> \ No newline at end of file diff --git a/interface/web/admin/form/server_config.tform.php b/interface/web/admin/form/server_config.tform.php index 027328d..24f3f18 100644 --- a/interface/web/admin/form/server_config.tform.php +++ b/interface/web/admin/form/server_config.tform.php @@ -138,11 +138,13 @@ 'width' => '40', 'maxlength' => '255' ), - 'backup_dir_ftpread' => array( + 'backup_mode' => array( 'datatype' => 'VARCHAR', - 'formtype' => 'CHECKBOX', - 'default' => 'y', - 'value' => array(0 => 'n', 1 => 'y') + 'formtype' => 'SELECT', + 'default' => 'userzip', + 'value' => array('userzip' => 'backup_mode_userzip', 'rootgz' => 'backup_mode_rootgz'), + 'width' => '40', + 'maxlength' => '255' ), ################################## # ENDE Datatable fields diff --git a/interface/web/admin/lib/lang/en_server_config.lng b/interface/web/admin/lib/lang/en_server_config.lng index ec2e053..4cce275 100644 --- a/interface/web/admin/lib/lang/en_server_config.lng +++ b/interface/web/admin/lib/lang/en_server_config.lng @@ -87,7 +87,9 @@ $wb["CA_path_txt"] = 'CA Path'; $wb["CA_pass_txt"] = 'CA passphrase'; $wb["fastcgi_config_syntax_txt"] = 'FastCGI config syntax'; -$wb["backup_dir_ftpread_txt"] = 'Backup dir. readable for website FTP users.'; +$wb["backup_mode_txt"] = 'Backup mode'; +$wb["backup_mode_userzip"] = 'Backup web files owned by web user as zip'; +$wb["backup_mode_rootgz"] = 'Backup all files in web directory as root user'; $wb["server_type_txt"] = 'Server Type'; $wb["nginx_vhost_conf_dir_txt"] = 'Nginx Vhost config dir'; $wb["nginx_vhost_conf_enabled_dir_txt"] = 'Nginx Vhost config enabled dir'; @@ -153,7 +155,6 @@ $wb["add_web_users_to_sshusers_group_txt"] = 'Add web users to -sshusers- group'; $wb["connect_userid_to_webid_txt"] = 'Connect Linux userid to webid'; $wb["connect_userid_to_webid_start_txt"] = 'Start ID for userid/webid connect'; - $wb["realtime_blackhole_list_txt"] = 'Real-time Blackhole List'; $wb["realtime_blackhole_list_note_txt"] = '(Separate RBL\'s with commas)'; ?> \ No newline at end of file diff --git a/interface/web/admin/templates/server_config_server_edit.htm b/interface/web/admin/templates/server_config_server_edit.htm index 8f42d8c..7b777bf 100644 --- a/interface/web/admin/templates/server_config_server_edit.htm +++ b/interface/web/admin/templates/server_config_server_edit.htm @@ -43,11 +43,11 @@ <input name="backup_dir" id="backup_dir" value="{tmpl_var name='backup_dir'}" size="40" maxlength="255" type="text" class="textInput" /> </div> <div class="ctrlHolder"> - <p class="label">{tmpl_var name='backup_dir_ftpread_txt'}</p> - <div class="multiField"> - {tmpl_var name='backup_dir_ftpread'} - </div> - </div> + <label for="backup_mode">{tmpl_var name='backup_mode_txt'}</label> + <select name="backup_mode" id="backup_mode" class="selectInput"> + {tmpl_var name='backup_mode'} + </select> + </div> </fieldset> <input type="hidden" name="id" value="{tmpl_var name='id'}"> diff --git a/interface/web/js/scrigo.js.php b/interface/web/js/scrigo.js.php index 94b7075..f8da538 100644 --- a/interface/web/js/scrigo.js.php +++ b/interface/web/js/scrigo.js.php @@ -269,6 +269,12 @@ } } +function confirm_action(link,confirmation) { + if(window.confirm(confirmation)) { + loadContent(link); + } +} + function loadContentInto(elementid,pagename) { var pageContentObject2 = jQuery.ajax({ type: "GET", url: pagename, diff --git a/interface/web/mail/mail_user_edit.php b/interface/web/mail/mail_user_edit.php index f57a4e8..398bf12 100644 --- a/interface/web/mail/mail_user_edit.php +++ b/interface/web/mail/mail_user_edit.php @@ -231,14 +231,14 @@ // Spamfilter policy $policy_id = intval($this->dataRecord["policy"]); if($policy_id > 0) { - $tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".mysql_real_escape_string($this->dataRecord["email"])."'"); + $tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".$app->db->quote($this->dataRecord["email"])."'"); if($tmp_user["id"] > 0) { // There is already a record that we will update $app->db->datalogUpdate('spamfilter_users', "policy_id = $policy_id", 'id', $tmp_user["id"]); } else { // We create a new record $insert_data = "(`sys_userid`, `sys_groupid`, `sys_perm_user`, `sys_perm_group`, `sys_perm_other`, `server_id`, `priority`, `policy_id`, `email`, `fullname`, `local`) - VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".mysql_real_escape_string($this->dataRecord["email"])."', '".mysql_real_escape_string($this->dataRecord["email"])."', 'Y')"; + VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".$app->db->quote($this->dataRecord["email"])."', '".$app->db->quote($this->dataRecord["email"])."', 'Y')"; $app->db->datalogInsert('spamfilter_users', $insert_data, 'id'); } } // endif spamfilter policy @@ -266,7 +266,7 @@ // Spamfilter policy $policy_id = intval($this->dataRecord["policy"]); - $tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".mysql_real_escape_string($this->dataRecord["email"])."'"); + $tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".$app->db->quote($this->dataRecord["email"])."'"); if($policy_id > 0) { if($tmp_user["id"] > 0) { // There is already a record that we will update @@ -274,7 +274,7 @@ } else { // We create a new record $insert_data = "(`sys_userid`, `sys_groupid`, `sys_perm_user`, `sys_perm_group`, `sys_perm_other`, `server_id`, `priority`, `policy_id`, `email`, `fullname`, `local`) - VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".mysql_real_escape_string($this->dataRecord["email"])."', '".mysql_real_escape_string($this->dataRecord["email"])."', 'Y')"; + VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".$app->db->quote($this->dataRecord["email"])."', '".$app->db->quote($this->dataRecord["email"])."', 'Y')"; $app->db->datalogInsert('spamfilter_users', $insert_data, 'id'); } }else { diff --git a/interface/web/sites/database_edit.php b/interface/web/sites/database_edit.php index e066943..501c650 100644 --- a/interface/web/sites/database_edit.php +++ b/interface/web/sites/database_edit.php @@ -91,20 +91,6 @@ $tmp = $app->db->queryOneRecord("SELECT server_name FROM server WHERE server_id = $client[default_webserver]"); $app->tpl->setVar("server_id","<option value='$client[default_webserver]'>$tmp[server_name]</option>"); unset($tmp); - - // Fill the client select field - $sql = "SELECT groupid, name FROM sys_group, client WHERE sys_group.client_id = client.client_id AND client.parent_client_id = ".$client['client_id']." ORDER BY name"; - $clients = $app->db->queryAllRecords($sql); - $tmp = $app->db->queryOneRecord("SELECT groupid FROM sys_group WHERE client_id = ".$client['client_id']); - $client_select = '<option value="'.$tmp['groupid'].'">'.$client['contact_name'].'</option>'; - //$tmp_data_record = $app->tform->getDataRecord($this->id); - if(is_array($clients)) { - foreach( $clients as $client) { - $selected = @(is_array($this->dataRecord) && ($client["groupid"] == $this->dataRecord['client_group_id'] || $client["groupid"] == $this->dataRecord['sys_groupid']))?'SELECTED':''; - $client_select .= "<option value='$client[groupid]' $selected>$client[name]</option>\r\n"; - } - } - $app->tpl->setVar("client_group_id",$client_select); } else { @@ -116,33 +102,6 @@ $tmp = $app->db->queryOneRecord("SELECT server_id FROM server WHERE web_server = 1 ORDER BY server_name LIMIT 0,1"); $server_id = $tmp['server_id']; } - - $sql = "SELECT ip_address FROM server_ip WHERE server_id = $server_id"; - $ips = $app->db->queryAllRecords($sql); - $ip_select = "<option value='*'>*</option>"; - //$ip_select = ""; - if(is_array($ips)) { - foreach( $ips as $ip) { - $selected = ($ip["ip_address"] == $this->dataRecord["ip_address"])?'SELECTED':''; - $ip_select .= "<option value='$ip[ip_address]' $selected>$ip[ip_address]</option>\r\n"; - } - } - $app->tpl->setVar("ip_address",$ip_select); - unset($tmp); - unset($ips); - - // Fill the client select field - $sql = "SELECT groupid, name FROM sys_group WHERE client_id > 0 ORDER BY name"; - $clients = $app->db->queryAllRecords($sql); - $client_select = "<option value='0'></option>"; - //$tmp_data_record = $app->tform->getDataRecord($this->id); - if(is_array($clients)) { - foreach( $clients as $client) { - $selected = @(is_array($this->dataRecord) && ($client["groupid"] == $this->dataRecord['client_group_id'] || $client["groupid"] == $this->dataRecord['sys_groupid']))?'SELECTED':''; - $client_select .= "<option value='$client[groupid]' $selected>$client[name]</option>\r\n"; - } - } - $app->tpl->setVar("client_group_id",$client_select); } @@ -212,9 +171,6 @@ } } - - // Clients may not set the client_group_id, so we unset them if user is not a admin and the client is not a reseller - if(!$app->auth->has_clients($_SESSION['s']['user']['userid'])) unset($this->dataRecord["client_group_id"]); } @@ -224,10 +180,8 @@ function onBeforeUpdate() { global $app, $conf, $interfaceConf; - /* - * If the names should be restricted -> do it! - */ - + //* Site shell not be empty + if($this->dataRecord['parent_domain_id'] == 0) $app->tform->errorMessage .= $app->tform->lng("database_site_error_empty").'<br />'; //* Get the database name and database user prefix $app->uses('getconf'); @@ -290,6 +244,9 @@ function onBeforeInsert() { global $app, $conf, $interfaceConf; + //* Site shell not be empty + if($this->dataRecord['parent_domain_id'] == 0) $app->tform->errorMessage .= $app->tform->lng("database_site_error_empty").'<br />'; + //* Database username and database name shall not be empty if($this->dataRecord['database_name'] == '') $app->tform->errorMessage .= $app->tform->wordbook["database_name_error_empty"].'<br />'; if($this->dataRecord['database_user'] == '') $app->tform->errorMessage .= $app->tform->wordbook["database_user_error_empty"].'<br />'; @@ -330,31 +287,33 @@ function onAfterInsert() { global $app, $conf; - - // make sure that the record belongs to the clinet group and not the admin group when a dmin inserts it - // also make sure that the user can not delete domain created by a admin - if($_SESSION["s"]["user"]["typ"] == 'admin' && isset($this->dataRecord["client_group_id"])) { - $client_group_id = intval($this->dataRecord["client_group_id"]); - $app->db->query("UPDATE web_database SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE database_id = ".$this->id); - } - if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) { - $client_group_id = intval($this->dataRecord["client_group_id"]); - $app->db->query("UPDATE web_database SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE database_id = ".$this->id); + + if($this->dataRecord["parent_domain_id"] > 0) { + $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".intval($this->dataRecord["parent_domain_id"])); + + //* The Database user shall be owned by the same group then the website + $sys_groupid = $web['sys_groupid']; + $backup_interval = $web['backup_interval']; + $backup_copies = $web['backup_copies']; + + $sql = "UPDATE web_database SET sys_groupid = '$sys_groupid', backup_interval = '$backup_interval', backup_copies = '$backup_copies' WHERE database_id = ".$this->id; + $app->db->query($sql); } } function onAfterUpdate() { global $app, $conf; - // make sure that the record belongs to the client group and not the admin group when a admin inserts it - // also make sure that the user can not delete domain created by a admin - if($_SESSION["s"]["user"]["typ"] == 'admin' && isset($this->dataRecord["client_group_id"])) { - $client_group_id = intval($this->dataRecord["client_group_id"]); - $app->db->query("UPDATE web_database SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE database_id = ".$this->id); - } - if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) { - $client_group_id = intval($this->dataRecord["client_group_id"]); - $app->db->query("UPDATE web_database SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE database_id = ".$this->id); + if($this->dataRecord["parent_domain_id"] > 0) { + $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".intval($this->dataRecord["parent_domain_id"])); + + //* The Database user shall be owned by the same group then the website + $sys_groupid = $web['sys_groupid']; + $backup_interval = $web['backup_interval']; + $backup_copies = $web['backup_copies']; + + $sql = "UPDATE web_database SET sys_groupid = '$sys_groupid', backup_interval = '$backup_interval', backup_copies = '$backup_copies' WHERE database_id = ".$this->id; + $app->db->query($sql); } } diff --git a/interface/web/sites/form/database.tform.php b/interface/web/sites/form/database.tform.php index ffb711f..9bef109 100644 --- a/interface/web/sites/form/database.tform.php +++ b/interface/web/sites/form/database.tform.php @@ -69,6 +69,17 @@ ), 'value' => '' ), + 'parent_domain_id' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'SELECT', + 'default' => '', + 'datasource' => array ( 'type' => 'SQL', + 'querystring' => "SELECT domain_id,domain FROM web_domain WHERE type = 'vhost' AND {AUTHSQL} ORDER BY domain", + 'keyfield'=> 'domain_id', + 'valuefield'=> 'domain' + ), + 'value' => array('0' => $app->tform->lng('select_site_txt')) + ), 'type' => array ( 'datatype' => 'VARCHAR', 'formtype' => 'SELECT', diff --git a/interface/web/sites/form/web_domain.tform.php b/interface/web/sites/form/web_domain.tform.php index 34cbbc8..241e6ea 100644 --- a/interface/web/sites/form/web_domain.tform.php +++ b/interface/web/sites/form/web_domain.tform.php @@ -431,7 +431,7 @@ ) ); -if($_SESSION["s"]["user"]["typ"] == 'admin') { +// if($_SESSION["s"]["user"]["typ"] == 'admin') { //* Backup $form["tabs"]['backup'] = array ( @@ -458,10 +458,17 @@ ################################## # ENDE Datatable fields ################################## + ), + 'plugins' => array ( + 'backup_records' => array ( + 'class' => 'plugin_backuplist', + 'options' => array( + ) + ) ) ); -} +// } if($_SESSION["s"]["user"]["typ"] == 'admin') { diff --git a/interface/web/sites/lib/lang/en_database.lng b/interface/web/sites/lib/lang/en_database.lng index 48d7ea3..abf302e 100644 --- a/interface/web/sites/lib/lang/en_database.lng +++ b/interface/web/sites/lib/lang/en_database.lng @@ -22,4 +22,9 @@ $wb["database_charset_change_txt"] = 'The database charset can not be changed'; $wb["database_name_error_len"] = 'Database name - {db} - too long. The max. database name length incl. prefix is 64 chars.'; $wb["database_user_error_len"] = 'Database username - {user}- too long. The max. database username length incl. prefix is 16 chars.'; +$wb["parent_domain_id_txt"] = 'Site'; +$wb["database_site_error_empty"] = 'Select the site to which the database belongs.'; +$wb["select_site_txt"] = '- Select Site -'; +$wb["btn_save_txt"] = 'Save'; +$wb["btn_cancel_txt"] = 'Cancel'; ?> diff --git a/interface/web/sites/lib/lang/en_web_backup_list.lng b/interface/web/sites/lib/lang/en_web_backup_list.lng new file mode 100644 index 0000000..1ac6687 --- /dev/null +++ b/interface/web/sites/lib/lang/en_web_backup_list.lng @@ -0,0 +1,16 @@ +<?php +$wb['list_head_txt'] = 'Existing backups'; +$wb['date_txt'] = 'Date'; +$wb['backup_type_txt'] = 'Type'; +$wb['filename_txt'] = 'Backup file'; +$wb['restore_backup_txt'] = 'Restore backup'; +$wb['download_backup_txt'] = 'Download backup'; +$wb['download_info_txt'] = 'The backup file will be available for download in the backup folder of the website in a few minutes.'; +$wb['restore_info_txt'] = 'Restore of the backup has been started. This action takes several minutes to be completed.'; +$wb['restore_confirm_txt'] = 'Restoring will overwrite existing files in your website. Do you really want to restore this backup?'; +$wb['download_pending_txt'] = 'There is already a pending backup download job.'; +$wb['restore_pending_txt'] = 'There is already a pending backup restore job.'; +$wb['backup_type_mysql'] = 'MySQL Database'; +$wb['backup_type_web'] = 'Website files'; + +?> \ No newline at end of file diff --git a/interface/web/sites/templates/database_edit.htm b/interface/web/sites/templates/database_edit.htm index 99e1d07..59b1bff 100644 --- a/interface/web/sites/templates/database_edit.htm +++ b/interface/web/sites/templates/database_edit.htm @@ -20,21 +20,13 @@ </select> </tmpl_if> </div> - <div class="ctrlHolder"> - <label for="client_group_id">{tmpl_var name='client_txt'}</label> - <select name="client_group_id" id="client_group_id" class="selectInput"> - {tmpl_var name='client_group_id'} - </select> - </div> </tmpl_if> - <tmpl_if name="is_reseller"> - <div class="ctrlHolder"> - <label for="client_group_id">{tmpl_var name='client_txt'}</label> - <select name="client_group_id" id="client_group_id" class="selectInput"> - {tmpl_var name='client_group_id'} + <div class="ctrlHolder"> + <label for="parent_domain_id">{tmpl_var name='parent_domain_id_txt'}</label> + <select name="parent_domain_id" id="parent_domain_id" class="selectInput"> + {tmpl_var name='parent_domain_id'} </select> - </div> - </tmpl_if> + </div> <div class="ctrlHolder"> <label for="type">{tmpl_var name='type_txt'}</label> <select name="type" id="type" class="selectInput formLengthHalf"> diff --git a/interface/web/sites/templates/web_backup_list.htm b/interface/web/sites/templates/web_backup_list.htm new file mode 100644 index 0000000..7d91c23 --- /dev/null +++ b/interface/web/sites/templates/web_backup_list.htm @@ -0,0 +1,40 @@ +<tmpl_if name="msg"> + <div id="OKMsg"><p><tmpl_var name="msg"></p></div> +</tmpl_if> +<tmpl_if name="error"> + <div id="errorMsg"><h3>ERROR</h3><ol><tmpl_var name="error"></ol></div> +</tmpl_if> +<h3><tmpl_var name="list_head_txt"></h3> + <div class="pnl_listarea"> + <fieldset><legend><tmpl_var name="list_head_txt"></legend> + <table class="list"> + <thead> + <tr> + <th class="tbl_col_date" scope="col"><tmpl_var name="date_txt"></th> + <th class="tbl_col_date" scope="col"><tmpl_var name="backup_type_txt"></th> + <th class="tbl_col_filename" scope="col"><tmpl_var name="filename_txt"></th> + <th class="tbl_col_buttons" scope="col"> </th> + </tr> + </thead> + <tbody> + <tmpl_loop name="records"> + <tr class="tbl_row_<tmpl_if name='__EVEN__'}even<tmpl_else>uneven</tmpl_if>"> + <td class="tbl_col_date">{tmpl_var name="date"}</td> + <td class="tbl_col_date">{tmpl_var name="backup_type"}</td> + <td class="tbl_col_filename">{tmpl_var name="filename"}</td> + <td class="tbl_col_buttons" style="width:300px;"> + <div class="buttons" > + <button class="iconstxt icoRestore" type="button" onClick="confirm_action('sites/web_domain_edit.php?id={tmpl_var name='parent_id'}&next_tab=backup&backup_action=restore&backup_id={tmpl_var name='backup_id'}','{tmpl_var name='restore_confirm_txt'}');"> + <span>{tmpl_var name="restore_backup_txt"}</span> + </button> + <button class="iconstxt icoDownload" type="button" onClick="loadContent('sites/web_domain_edit.php?id={tmpl_var name='parent_id'}&next_tab=backup&backup_action=download&backup_id={tmpl_var name='backup_id'}');"> + <span>{tmpl_var name="download_backup_txt"}</span> + </button> + </div> + </td> + </tr> + </tmpl_loop> + </tbody> + </table> + </fieldset> + </div> diff --git a/interface/web/sites/templates/web_domain_backup.htm b/interface/web/sites/templates/web_domain_backup.htm index 6b8c627..14e3a8f 100644 --- a/interface/web/sites/templates/web_domain_backup.htm +++ b/interface/web/sites/templates/web_domain_backup.htm @@ -18,6 +18,8 @@ </select> </div> </fieldset> + + {tmpl_var name='backup_records'} <input type="hidden" name="id" value="{tmpl_var name='id'}"> diff --git a/interface/web/sites/tools.inc.php b/interface/web/sites/tools.inc.php index 38e8804..316abe6 100644 --- a/interface/web/sites/tools.inc.php +++ b/interface/web/sites/tools.inc.php @@ -96,7 +96,7 @@ } } /* get the name of the client */ - $tmp = $app->db->queryOneRecord("SELECT client_id FROM sys_group WHERE groupid = " . $client_group_id); + $tmp = $app->db->queryOneRecord("SELECT client_id FROM sys_group WHERE groupid = " . intval($client_group_id)); $clientID = $tmp['client_id']; if ($clientID == '') $clientID = '0'; return $clientID; diff --git a/interface/web/sites/web_domain_del.php b/interface/web/sites/web_domain_del.php index a69923f..f84f4f1 100644 --- a/interface/web/sites/web_domain_del.php +++ b/interface/web/sites/web_domain_del.php @@ -91,6 +91,12 @@ $app->db->datalogDelete('webdav_user','webdav_user_id',$rec['webdav_user_id']); } + //* Delete all records that belog to this web + $records = $app->db->queryAllRecords("SELECT backup_id FROM web_backup WHERE parent_domain_id = '".intval($this->id)."'"); + foreach($records as $rec) { + $app->db->datalogDelete('web_backup','backup_id',$rec['backup_id']); + } + //* Delete all web folders $records = $app->db->queryAllRecords("SELECT web_folder_id FROM web_folder WHERE parent_domain_id = '".intval($this->id)."'"); foreach($records as $rec) { diff --git a/interface/web/sites/web_domain_edit.php b/interface/web/sites/web_domain_edit.php index b6236bd..91b965c 100644 --- a/interface/web/sites/web_domain_edit.php +++ b/interface/web/sites/web_domain_edit.php @@ -607,6 +607,14 @@ } unset($records); unset($rec); + + //* Update all databases + $records = $app->db->queryAllRecords("SELECT database_id FROM web_database WHERE parent_domain_id = ".$this->id); + foreach($records as $rec) { + $app->db->datalogUpdate('web_database', "sys_userid = '".$web_rec['sys_userid']."', sys_groupid = '".$web_rec['sys_groupid']."'", 'database_id', $rec['database_id']); + } + unset($records); + unset($rec); } @@ -638,6 +646,21 @@ $sql = "UPDATE web_domain SET php_open_basedir = '$php_open_basedir' WHERE domain_id = ".$this->id; $app->db->query($sql); } + + //* Change database backup options when web backup options have been changed + if(isset($this->dataRecord['backup_interval']) && ($this->dataRecord['backup_interval'] != $this->oldDataRecord['backup_interval'] || $this->dataRecord['backup_copies'] != $this->oldDataRecord['backup_copies'])) { + //* Update all databases + $backup_interval = $this->dataRecord['backup_interval']; + $backup_copies = $this->dataRecord['backup_copies']; + $records = $app->db->queryAllRecords("SELECT database_id FROM web_database WHERE parent_domain_id = ".$this->id); + foreach($records as $rec) { + $app->db->datalogUpdate('web_database', "backup_interval = '$backup_interval', backup_copies = '$backup_copies'", 'database_id', $rec['database_id']); + } + unset($records); + unset($rec); + unset($backup_copies); + unset($backup_interval); + } } diff --git a/interface/web/themes/default/css/screen/content_ispc.css b/interface/web/themes/default/css/screen/content_ispc.css index 9f53502..9e65bd4 100644 --- a/interface/web/themes/default/css/screen/content_ispc.css +++ b/interface/web/themes/default/css/screen/content_ispc.css @@ -366,6 +366,8 @@ .iconstxt.icoAdd { background-image: url(../../icons/x16/plus_circle_frame.png); } .iconstxt.icoKey { background-image: url("../../icons/x16/key.png"); } .iconstxt.icoDelete { background-image: url("../../icons/x16/minus_circle_frame.png"); } + .iconstxt.icoDownload { background-image: url("../../icons/x16/arrow_270.png"); } + .iconstxt.icoRestore { background-image: url("../../icons/x16/arrow_circle_225.png"); } /* Button with icon and without text */ .icons16 span { display: none; } diff --git a/server/cron_daily.php b/server/cron_daily.php index 67d9945..87ba2ee 100644 --- a/server/cron_daily.php +++ b/server/cron_daily.php @@ -1,7 +1,7 @@ <?php /* -Copyright (c) 2007, Till Brehm, projektfarm Gmbh +Copyright (c) 2007-2012, Till Brehm, projektfarm Gmbh All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -480,6 +480,11 @@ $server_config = $app->getconf->get_server_config($conf['server_id'], 'server'); $backup_dir = $server_config['backup_dir']; +$backup_mode = $server_config['backup_mode']; +if($backup_mode == '') $backup_mode = 'userzip'; + +$web_config = $app->getconf->get_server_config($conf['server_id'], 'web'); +$http_server_user = $web_config['user']; if($backup_dir != '') { @@ -495,12 +500,12 @@ chmod(escapeshellcmd($backup_dir), $backup_dir_permissions); } - $sql = "SELECT * FROM web_domain WHERE type = 'vhost'"; + $sql = "SELECT * FROM web_domain WHERE server_id = '".$conf['server_id']."' AND type = 'vhost' AND backup_interval != 'none'"; $records = $app->db->queryAllRecords($sql); if(is_array($records)) { foreach($records as $rec) { - // Create a backup + //* Do the website backup if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) { $web_path = $rec['document_root']; @@ -510,46 +515,71 @@ $web_backup_dir = $backup_dir.'/web'.$web_id; if(!is_dir($web_backup_dir)) mkdir($web_backup_dir, 0750); chmod($web_backup_dir, 0750); - if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') { - chown($web_backup_dir, $rec['system_user']); - chgrp($web_backup_dir, $rec['system_group']); - } else { + //if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') { + chown($web_backup_dir, $rec['system_user']); + chgrp($web_backup_dir, $rec['system_group']); + /*} else { chown($web_backup_dir, 'root'); chgrp($web_backup_dir, 'root'); + }*/ + if($backup_mode == 'userzip') { + //* Create a .zip backup as web user and include also files owned by apache / nginx user + $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.zip'; + exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -group '.escapeshellarg($web_group).' -print 2> /dev/null | zip --exclude=backup\* --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@'); + exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -user '.escapeshellarg($http_server_user).' -print 2> /dev/null | zip --exclude=backup\* --update --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@'); + } else { + //* Create a tar.gz backup as root user + $web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.tar.gz'; + exec('tar pczf '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' --exclude=backup\* --directory '.escapeshellarg($web_path).' .'); } - exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -group '.escapeshellarg($web_group).' -print | zip -y '.escapeshellarg($web_backup_dir.'/web.zip').' -@'); - chown($web_backup_dir.'/web.zip', $rec['system_user']); - chgrp($web_backup_dir.'/web.zip', $rec['system_group']); - chmod($web_backup_dir.'/web.zip', 0750); + chown($web_backup_dir.'/'.$web_backup_file, 'root'); + chgrp($web_backup_dir.'/'.$web_backup_file, 'root'); + chmod($web_backup_dir.'/'.$web_backup_file, 0750); - // Rename or remove old backups + //* Insert web backup record in database + $insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",".$web_id.",'web','".$backup_mode."',".time().",'".$app->db->quote($web_backup_file)."')"; + $app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id'); + + //* Remove old backups $backup_copies = intval($rec['backup_copies']); - //* delete any older backup copies that previously existed - for ($n = $backup_copies; $n <= 10; $n++) { - if(is_file($web_backup_dir.'/web.'.$n.'.zip')) unlink($web_backup_dir.'/web.'.$n.'.zip'); - } - - for($n = $backup_copies - 1; $n >= 1; $n--) { - if(is_file($web_backup_dir.'/web.'.$n.'.zip')) { - rename($web_backup_dir.'/web.'.$n.'.zip',$web_backup_dir.'/web.'.($n+1).'.zip'); + $dir_handle = dir($web_backup_dir); + $files = array(); + while (false !== ($entry = $dir_handle->read())) { + if($entry != '.' && $entry != '..' && substr($entry,0,3) == 'web' && is_file($web_backup_dir.'/'.$entry)) { + $files[] = $entry; } } - - if(is_file($web_backup_dir.'/web.zip')) rename($web_backup_dir.'/web.zip',$web_backup_dir.'/web.1.zip'); - - // Create backupdir symlink - if(is_link($web_path.'/backup')) unlink($web_path.'/backup'); - symlink($web_backup_dir,$web_path.'/backup'); - // chmod($web_path.'/backup', 0755); - chown($web_path.'/backup', $rec['system_user']); - chgrp($web_path.'/backup', $rec['system_group']); + $dir_handle->close(); + + rsort($files); + + for ($n = $backup_copies; $n <= 10; $n++) { + if(isset($files[$n]) && is_file($web_backup_dir.'/'.$files[$n])) { + unlink($web_backup_dir.'/'.$files[$n]); + $sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'"; + $tmp = $app->dbmaster->queryOneRecord($sql); + $app->dbmaster->datalogDelete('web_backup', 'backup_id', $tmp['backup_id']); + } + } + + unset($files); + unset($dir_handle); + + //* Remove backupdir symlink and create as directory instead + if(is_link($web_path.'/backup')) { + unlink($web_path.'/backup'); + } + if(!is_dir($web_path.'/backup')) { + mkdir($web_path.'/backup'); + chown($web_path.'/backup', $rec['system_user']); + chgrp($web_path.'/backup', $rec['system_group']); + } } /* If backup_interval is set to none and we have a backup directory for the website, then remove the backups */ - if($rec['backup_interval'] == 'none') { $web_id = $rec['domain_id']; $web_user = $rec['system_user']; @@ -560,6 +590,78 @@ } } } + + $sql = "SELECT * FROM web_database WHERE server_id = '".$conf['server_id']."' AND backup_interval != 'none'"; + $records = $app->db->queryAllRecords($sql); + if(is_array($records)) { + + include('lib/mysql_clientdb.conf'); + + foreach($records as $rec) { + + //* Do the database backup + if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) { + + $web_id = $rec['parent_domain_id']; + $db_backup_dir = $backup_dir.'/web'.$web_id; + if(!is_dir($web_backup_dir)) mkdir($web_backup_dir, 0750); + chmod($web_backup_dir, 0750); + chown($web_backup_dir, 'root'); + chgrp($web_backup_dir, 'root'); + + //* Do the mysql database backup with mysqldump + $db_id = $rec['database_id']; + $db_name = $rec['database_name']; + $db_backup_file = 'db_'.$db_name.'_'.date('Y-m-d_H-i').'.sql'; + $command = "mysqldump -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' -c --add-drop-table --create-options --quick --result-file='".$db_backup_dir.'/'.$db_backup_file."' '".$db_name."'"; + exec($command); + + //* Compress the backup with gzip + exec("gzip -c '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file)."' > '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file).".gz'"); + chmod($db_backup_dir.'/'.$db_backup_file.'.gz', 0750); + chown($db_backup_dir.'/'.$db_backup_file.'.gz', fileowner($db_backup_dir)); + chgrp($db_backup_dir.'/'.$db_backup_file.'.gz', filegroup($db_backup_dir)); + + //* Insert web backup record in database + $insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",$web_id,'mysql','sqlgz',".time().",'".$app->db->quote($db_backup_file).".gz')"; + $app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id'); + + //* Remove the uncompressed file + unlink($db_backup_dir.'/'.$db_backup_file); + + //* Remove old backups + $backup_copies = intval($rec['backup_copies']); + + $dir_handle = dir($db_backup_dir); + $files = array(); + while (false !== ($entry = $dir_handle->read())) { + if($entry != '.' && $entry != '..' && substr($entry,0,2) == 'db' && is_file($db_backup_dir.'/'.$entry)) { + $files[] = $entry; + } + } + $dir_handle->close(); + + rsort($files); + + for ($n = $backup_copies; $n <= 10; $n++) { + if(isset($files[$n]) && is_file($db_backup_dir.'/'.$files[$n])) { + unlink($db_backup_dir.'/'.$files[$n]); + $sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'"; + $tmp = $app->dbmaster->queryOneRecord($sql); + $app->dbmaster->datalogDelete('web_backup', 'backup_id', $tmp['backup_id']); + } + } + + unset($files); + unset($dir_handle); + } + } + + unset($clientdb_host); + unset($clientdb_user); + unset($clientdb_password); + + } } diff --git a/server/lib/classes/db_mysql.inc.php b/server/lib/classes/db_mysql.inc.php index 18f09bf..54561c0 100644 --- a/server/lib/classes/db_mysql.inc.php +++ b/server/lib/classes/db_mysql.inc.php @@ -1,4 +1,3 @@ - <?php /* Copyright (c) 2005, Till Brehm, projektfarm Gmbh @@ -226,10 +225,10 @@ if(is_array($record_old) && count($record_old) > 0) { foreach($record_old as $key => $val) { // if(!isset($record_new[$key]) || $record_new[$key] != $val) { - if($record_new[$key] != $val) { + if(@$record_new[$key] != $val) { // Record has changed $diffrec_full['old'][$key] = $val; - $diffrec_full['new'][$key] = $record_new[$key]; + $diffrec_full['new'][$key] = @$record_new[$key]; $diff_num++; } else { $diffrec_full['old'][$key] = $val; @@ -593,4 +592,4 @@ } - ?> + ?> \ No newline at end of file diff --git a/server/lib/classes/modules.inc.php b/server/lib/classes/modules.inc.php index 2c5b349..ef600bd 100644 --- a/server/lib/classes/modules.inc.php +++ b/server/lib/classes/modules.inc.php @@ -229,8 +229,46 @@ $app->log('Processed datalog_id '.$d['datalog_id'],LOGLEVEL_DEBUG); } } + } + + function processActions() { + global $app,$conf; + //* get the server_id of the local server + $server_id = intval($conf["server_id"]); + include_once (SCRIPT_PATH."/lib/remote_action.inc.php"); + + //* SQL query to get all pending actions + $sql = "SELECT action_id, action_type, action_param " . + "FROM sys_remoteaction " . + "WHERE server_id = " . $server_id . " ". + " AND action_id > " . intval($maxid_remote_action) . " ". + "ORDER BY action_id"; + + $actions = $app->dbmaster->queryAllRecords($sql); + + if(is_array($actions)) { + foreach($actions as $action) { + + //* Raise the action + $state = $app->plugins->raiseAction($action['action_type'],$action['action_param']); + + //* Update the action state + $sql = "UPDATE sys_remoteaction " . + "SET action_state = '" . $app->dbmaster->quote($state) . "' " . + "WHERE action_id = " . intval($action['action_id']); + $app->dbmaster->query($sql); + + /* + * Then save the maxid for the next time... + */ + $fp = fopen(ISPC_LIB_PATH."/remote_action.inc.php", 'wb'); + $content = '<?php' . "\n" . '$maxid_remote_action = ' . $action['action_id'] . ';' . "\n?>"; + fwrite($fp, $content); + fclose($fp); + } + } diff --git a/server/lib/classes/plugins.inc.php b/server/lib/classes/plugins.inc.php index ba12981..afef438 100644 --- a/server/lib/classes/plugins.inc.php +++ b/server/lib/classes/plugins.inc.php @@ -1,7 +1,7 @@ <?php /* -Copyright (c) 2007, Till Brehm, projektfarm Gmbh +Copyright (c) 2007-2012, Till Brehm, projektfarm Gmbh All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -32,6 +32,7 @@ var $available_events = array(); var $subscribed_events = array(); + var $subscribed_actions = array(); var $debug = false; /* @@ -126,6 +127,45 @@ unset($events); } + /* + This function is called by the plugin to register for an action + */ + + function registerAction($action_name,$plugin_name,$function_name) { + global $app; + $this->subscribed_actions[$action_name][] = array('plugin' => $plugin_name, 'function' => $function_name); + if($this->debug) $app->log("Registered function '$function_name' from plugin '$plugin_name' for action '$event_name'.",LOGLEVEL_DEBUG); + } + + + function raiseAction($action_name,$data) { + global $app; + + //* Get the subscriptions for this action + $actions = (isset($this->subscribed_actions[$action_name]))?$this->subscribed_actions[$action_name]:''; + if($this->debug) $app->log('Raised action: '.$action_name,LOGLEVEL_DEBUG); + + if(is_array($actions)) { + foreach($actions as $action) { + $plugin_name = $action['plugin']; + $function_name = $action['function']; + $state_out = 'ok'; + //* Call the processing function of the plugin + $app->log("Calling function '$function_name' from plugin '$plugin_name' raised by action '$action_name'.",LOGLEVEL_DEBUG); + $state = call_user_func(array($app->loaded_plugins[$plugin_name],$function_name),$action_name,$data); + //* ensure that we return the highest warning / error level if a error occured in one of the functions + if($state == 'warning' && $state_out != 'error') $state_out = 'warning'; + if($state == 'error') $state_out = 'error'; + unset($plugin_name); + unset($function_name); + } + } + unset($action); + unset($actions); + + return $state_out; + } + } ?> diff --git a/server/mods-available/remoteaction_core_module.inc.php b/server/mods-available/remoteaction_core_module.inc.php index 49294f4..5ee3fc6 100644 --- a/server/mods-available/remoteaction_core_module.inc.php +++ b/server/mods-available/remoteaction_core_module.inc.php @@ -1,6 +1,6 @@ <?php /* -Copyright (c) 2007-2010, Till Brehm, projektfarm Gmbh and Oliver Vogel www.muv.com +Copyright (c) 2007-2010, Till Brehm, projektfarm Gmbh, Oliver Vogel www.muv.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -35,7 +35,7 @@ //* This function is called during ispconfig installation to determine // if a symlink shall be created for this plugin. function onInstall() { - return true; + return false; } /* @@ -45,7 +45,8 @@ /* * Check for actions to execute */ - $this->_execActions(); + //* This module has been replaced by the new action framework. + // $this->_execActions(); } /* diff --git a/server/mods-available/web_module.inc.php b/server/mods-available/web_module.inc.php index 653940f..086b601 100644 --- a/server/mods-available/web_module.inc.php +++ b/server/mods-available/web_module.inc.php @@ -49,7 +49,10 @@ 'web_folder_delete', 'web_folder_user_insert', 'web_folder_user_update', - 'web_folder_user_delete'); + 'web_folder_user_delete', + 'web_backup_insert', + 'web_backup_update', + 'web_backup_delete'); //* This function is called during ispconfig installation to determine // if a symlink shall be created for this plugin. @@ -94,6 +97,7 @@ $app->modules->registerTableHook('webdav_user','web_module','process'); $app->modules->registerTableHook('web_folder','web_module','process'); $app->modules->registerTableHook('web_folder_user','web_module','process'); + $app->modules->registerTableHook('web_backup','web_module','process'); // Register service $app->services->registerService('httpd','web_module','restartHttpd'); @@ -139,6 +143,11 @@ if($action == 'u') $app->plugins->raiseEvent('web_folder_user_update',$data); if($action == 'd') $app->plugins->raiseEvent('web_folder_user_delete',$data); break; + case 'web_backup': + if($action == 'i') $app->plugins->raiseEvent('web_backup_insert',$data); + if($action == 'u') $app->plugins->raiseEvent('web_backup_update',$data); + if($action == 'd') $app->plugins->raiseEvent('web_backup_delete',$data); + break; } // end switch } // end function diff --git a/server/plugins-available/backup_plugin.inc.php b/server/plugins-available/backup_plugin.inc.php new file mode 100644 index 0000000..dfe3ddd --- /dev/null +++ b/server/plugins-available/backup_plugin.inc.php @@ -0,0 +1,134 @@ +<?php + +/* +Copyright (c) 2012, Till Brehm, ISPConfig UG +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of ISPConfig nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +class backup_plugin { + + var $plugin_name = 'backup_plugin'; + var $class_name = 'backup_plugin'; + + //* This function is called during ispconfig installation to determine + // if a symlink shall be created for this plugin. + public function onInstall() { + global $conf; + + return true; + + } + + + /* + This function is called when the plugin is loaded + */ + + public function onLoad() { + global $app; + + //* Register for actions + $app->plugins->registerAction('backup_download',$this->plugin_name,'backup_action'); + $app->plugins->registerAction('backup_restore',$this->plugin_name,'backup_action'); + + } + + //* Do a backup action + public function backup_action($action_name,$data) { + global $app,$conf; + + $backup_id = intval($data); + $backup = $app->db->queryOneRecord("SELECT * FROM web_backup WHERE backup_id = $backup_id"); + + if(is_array($backup)) { + + $app->uses('ini_parser,file,getconf'); + + $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".$backup['parent_domain_id']); + $server_config = $app->getconf->get_server_config($conf['server_id'], 'server'); + $backup_dir = $server_config['backup_dir'].'/web'.$web['domain_id']; + + //* Make backup available for download + if($action_name == 'backup_download') { + //* Copy the backup file to the backup folder of the website + if(file_exists($backup_dir.'/'.$backup['filename']) && !stristr($backup_dir.'/'.$backup['filename'],'..') && !stristr($backup_dir.'/'.$backup['filename'],'etc')) { + copy($backup_dir.'/'.$backup['filename'],$web['document_root'].'/backup/'.$backup['filename']); + chgrp($web['document_root'].'/backup/'.$backup['filename'],$web['system_group']); + $app->log('cp '.$backup_dir.'/'.$backup['filename'].' '.$web['document_root'].'/backup/'.$backup['filename'],LOGLEVEL_DEBUG); + } + } + + //* Restore a mysql backup + if($action_name == 'backup_restore' && $backup['backup_type'] == 'mysql') { + //* Load sql dump into db + include('lib/mysql_clientdb.conf'); + + if(file_exists($backup_dir.'/'.$backup['filename'])) { + $parts = explode('_',$backup['filename']); + $db_name = $parts[1]; + $command = "gunzip --stdout ".escapeshellarg($backup_dir.'/'.$backup['filename'])." | mysql -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' '".$db_name."'"; + exec($command); + } + unset($clientdb_host); + unset($clientdb_user); + unset($clientdb_password); + $app->log('Restored MySQL backup '.$backup_dir.'/'.$backup['filename'],LOGLEVEL_DEBUG); + } + + //* Restore a web backup + if($action_name == 'backup_restore' && $backup['backup_type'] == 'web') { + if($backup['backup_mode'] == 'userzip') { + if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'],'..') && !stristr($backup_dir.'/'.$backup['filename'],'etc')) { + if(file_exists($web['document_root'].'/backup/'.$backup['filename'])) rename($web['document_root'].'/backup/'.$backup['filename'],$web['document_root'].'/backup/'.$backup['filename'].'.bak'); + copy($backup_dir.'/'.$backup['filename'],$web['document_root'].'/backup/'.$backup['filename']); + chgrp($web['document_root'].'/backup/'.$backup['filename'],$web['system_group']); + //chown($web['document_root'].'/backup/'.$backup['filename'],$web['system_user']); + $command = 'sudo -u '.escapeshellarg($web['system_user']).' unzip -qq -o '.escapeshellarg($web['document_root'].'/backup/'.$backup['filename']).' -d '.escapeshellarg($web['document_root']).' 2> /dev/null'; + exec($command); + unlink($web['document_root'].'/backup/'.$backup['filename']); + if(file_exists($web['document_root'].'/backup/'.$backup['filename'].'.bak')) rename($web['document_root'].'/backup/'.$backup['filename'].'.bak',$web['document_root'].'/backup/'.$backup['filename']); + $app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'],LOGLEVEL_DEBUG); + } + } + if($backup['backup_mode'] == 'rootgz') { + if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'],'..') && !stristr($backup_dir.'/'.$backup['filename'],'etc')) { + $command = 'tar xzf '.escapeshellarg($backup_dir.'/'.$backup['filename']).' --directory '.escapeshellarg($web['document_root']); + exec($command); + $app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'],LOGLEVEL_DEBUG); + } + } + } + + } else { + $app->log('No backup with ID '.$backup_id.' found.',LOGLEVEL_DEBUG); + } + + return 'ok'; + } + +} // end class + +?> diff --git a/server/plugins-available/mysql_clientdb_plugin.inc.php b/server/plugins-available/mysql_clientdb_plugin.inc.php index 624ef2b..59a6cf4 100644 --- a/server/plugins-available/mysql_clientdb_plugin.inc.php +++ b/server/plugins-available/mysql_clientdb_plugin.inc.php @@ -100,7 +100,7 @@ if($valid == false) continue; if($action == 'GRANT') { - if(!$link->query("GRANT ALL ON ".$link->escape_string($database_name,$link).".* TO '".$link->escape_string($database_user,$link)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($database_password,$link)."';",$link)) $success = false; + if(!$link->query("GRANT ALL ON ".$link->escape_string($database_name).".* TO '".$link->escape_string($database_user)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($database_password)."';")) $success = false; } elseif($action == 'REVOKE') { //mysql_query("REVOKE ALL PRIVILEGES ON ".mysql_real_escape_string($database_name,$link).".* FROM '".mysql_real_escape_string($database_user,$link)."';",$link); } elseif($action == 'DROP') { @@ -108,7 +108,7 @@ } elseif($action == 'RENAME') { if(!$link->query("RENAME USER '".$link->escape_string($database_user)."'@'$db_host' TO '".$link->escape_string($database_rename_user)."'@'$db_host'")) $success = false; } elseif($action == 'PASSWORD') { - if(!$link->query("SET PASSWORD FOR '".$link->escape_string($database_user,$link)."'@'$db_host' = '".$link->escape_string($database_password,$link)."';",$link)) $success = false; + if(!$link->query("SET PASSWORD FOR '".$link->escape_string($database_user)."'@'$db_host' = '".$link->escape_string($database_password)."';")) $success = false; } } @@ -158,7 +158,7 @@ } $db_host = 'localhost'; - $link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name'],$link))."`.* TO '".$link->escape_string($data['new']['database_user'],$link)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'],$link)."';",$link); + $link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name']))."`.* TO '".$link->escape_string($data['new']['database_user'])."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'])."';"); } @@ -197,7 +197,7 @@ } $db_host = 'localhost'; - $link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name'],$link))."`.* TO '".$link->escape_string($data['new']['database_user'],$link)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'],$link)."';",$link); + $link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name']))."`.* TO '".$link->escape_string($data['new']['database_user'])."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'])."';"); // mysql_query("GRANT ALL ON ".mysql_real_escape_string($data["new"]["database_name"],$link).".* TO '".mysql_real_escape_string($data["new"]["database_user"],$link)."'@'$db_host' IDENTIFIED BY '".mysql_real_escape_string($data["new"]["database_password"],$link)."';",$link); //echo "GRANT ALL ON ".mysql_real_escape_string($data["new"]["database_name"]).".* TO '".mysql_real_escape_string($data["new"]["database_user"])."'@'$db_host' IDENTIFIED BY '".mysql_real_escape_string($data["new"]["database_password"])."';"; @@ -247,10 +247,10 @@ //* Change password if($data['new']['database_password'] != $data['old']['database_password']) { $db_host = 'localhost'; - $link->query("SET PASSWORD FOR '".$link->escape_string($data['new']['database_user'],$link)."'@'$db_host' = '".$link->escape_string($data['new']['database_password'],$link)."';",$link); + $link->query("SET PASSWORD FOR '".$link->escape_string($data['new']['database_user'])."'@'$db_host' = '".$link->escape_string($data['new']['database_password'])."';"); if($data['new']['remote_access'] == 'y') { - $this->process_host_list('PASSWORD', '', $data['new']['database_user'], $data['new']['database_password'], $data['new']['remote_ips']); + $this->process_host_list('PASSWORD', '', $data['new']['database_user'], $data['new']['database_password'], $data['new']['remote_ips'],$link); } $app->log('Changing MySQL user password for: '.$data['new']['database_user'],LOGLEVEL_DEBUG); } diff --git a/server/plugins-available/openvz_plugin.inc.php b/server/plugins-available/openvz_plugin.inc.php index ac9e0aa..9e0d75d 100644 --- a/server/plugins-available/openvz_plugin.inc.php +++ b/server/plugins-available/openvz_plugin.inc.php @@ -1,7 +1,7 @@ <?php /* -Copyright (c) 2011, Till Brehm, projektfarm Gmbh +Copyright (c) 2011-2012, Till Brehm, projektfarm Gmbh All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -63,6 +63,14 @@ $app->plugins->registerEvent('openvz_vm_insert',$this->plugin_name,'vm_insert'); $app->plugins->registerEvent('openvz_vm_update',$this->plugin_name,'vm_update'); $app->plugins->registerEvent('openvz_vm_delete',$this->plugin_name,'vm_delete'); + + //* Register for actions + $app->plugins->registerAction('openvz_start_vm',$this->plugin_name,'actions'); + $app->plugins->registerAction('openvz_stop_vm',$this->plugin_name,'actions'); + $app->plugins->registerAction('openvz_restart_vm',$this->plugin_name,'actions'); + $app->plugins->registerAction('openvz_create_ostpl',$this->plugin_name,'actions'); + + } @@ -149,6 +157,50 @@ } + function actions($action_name,$data) { + global $app, $conf; + + if ($action_name == 'openvz_start_vm') { + $veid = intval($data); + if($veid > 0) { + exec("vzctl start $veid"); + $app->log("Start VM: vzctl start $veid",LOGLEVEL_DEBUG); + } + return 'ok'; + } + if ($action_name == 'openvz_stop_vm') { + $veid = intval($data); + if($veid > 0) { + exec("vzctl stop $veid"); + $app->log("Stop VM: vzctl stop $veid",LOGLEVEL_DEBUG); + } + return 'ok'; + } + if ($action_name == 'openvz_restart_vm') { + $veid = intval($data); + if($veid > 0) { + exec("vzctl restart $veid"); + $app->log("Restart VM: vzctl restart $veid",LOGLEVEL_DEBUG); + } + return 'ok'; + } + if ($action_name == 'openvz_create_ostpl') { + $parts = explode(':',$data); + $veid = intval($parts[0]); + $template_cache_dir = '/vz/template/cache/'; + $template_name = escapeshellcmd($parts[1]); + if($veid > 0 && $template_name != '' && is_dir($template_cache_dir)) { + $command = "vzdump --suspend --compress --stdexcludes --dumpdir $template_cache_dir $veid"; + exec($command); + exec("mv ".$template_cache_dir."vzdump-openvz-".$veid."*.tgz ".$template_cache_dir.$template_name.".tar.gz"); + exec("rm -f ".$template_cache_dir."vzdump-openvz-".$veid."*.log"); + } + $app->log("Created OpenVZ OStemplate $template_name from VM $veid",LOGLEVEL_DEBUG); + return 'ok'; + } + + } + } // end class diff --git a/server/plugins-available/software_update_plugin.inc.php b/server/plugins-available/software_update_plugin.inc.php index 55feaee..a6b7162 100644 --- a/server/plugins-available/software_update_plugin.inc.php +++ b/server/plugins-available/software_update_plugin.inc.php @@ -1,7 +1,7 @@ <?php /* -Copyright (c) 2007, Till Brehm, projektfarm Gmbh +Copyright (c) 2007-2012, Till Brehm, projektfarm Gmbh, Oliver Vogel www.muv.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -35,7 +35,7 @@ //* This function is called during ispconfig installation to determine // if a symlink shall be created for this plugin. - function onInstall() { + public function onInstall() { global $conf; return true; @@ -47,29 +47,31 @@ This function is called when the plugin is loaded */ - function onLoad() { + public function onLoad() { global $app; /* Register for the events */ - //* Mailboxes $app->plugins->registerEvent('software_update_inst_insert',$this->plugin_name,'process'); //$app->plugins->registerEvent('software_update_inst_update',$this->plugin_name,'process'); //$app->plugins->registerEvent('software_update_inst_delete',$this->plugin_name,'process'); + //* Register for actions + $app->plugins->registerAction('os_update',$this->plugin_name,'os_update'); + } - function set_install_status($inst_id, $status) { + private function set_install_status($inst_id, $status) { global $app; $app->db->query("UPDATE software_update_inst SET status = '{$status}' WHERE software_update_inst_id = '{$inst_id}'"); $app->dbmaster->query("UPDATE software_update_inst SET status = '{$status}' WHERE software_update_inst_id = '{$inst_id}'"); } - function process($event_name,$data) { + public function process($event_name,$data) { global $app, $conf; //* Get the info of the package: @@ -271,6 +273,26 @@ } } + + //* Operating system update + public function os_update($action_name,$data) { + global $app; + + //** Debian and compatible Linux distributions + if(file_exists('/etc/debian_version')) { + exec("aptitude update"); + exec("aptitude safe-upgrade -y"); + $app->log('Execeuted Debian / Ubuntu update',LOGLEVEL_DEBUG); + } + + //** Gentoo Linux + if(file_exists('/etc/gentoo-release')) { + exec("glsa-check -f --nocolor affected"); + $app->log('Execeuted Gentoo update',LOGLEVEL_DEBUG); + } + + return 'ok'; + } } // end class diff --git a/server/server.php b/server/server.php index 869a621..3683f19 100644 --- a/server/server.php +++ b/server/server.php @@ -144,27 +144,26 @@ $tmp_num_records = $tmp_rec['number']; unset($tmp_rec); - + + //** Load required base-classes + $app->uses('modules,plugins,file,services'); + //** Load the modules that are in the mods-enabled folder + $app->modules->loadModules('all'); + //** Load the plugins that are in the plugins-enabled folder + $app->plugins->loadPlugins('all'); if ($tmp_num_records > 0) { - /* - There is something to do, triggert by the database -> do it! - */ - // Write the Log $app->log("Found $tmp_num_records changes, starting update process.", LOGLEVEL_DEBUG); - // Load required base-classes - $app->uses('modules,plugins,file,services'); - // Load the modules that are in the mods-enabled folder - $app->modules->loadModules('all'); - // Load the plugins that are in the plugins-enabled folder - $app->plugins->loadPlugins('all'); - // Go through the sys_datalog table and call the processing functions - // from the modules that are hooked on to the table actions + //** Go through the sys_datalog table and call the processing functions + //** from the modules that are hooked on to the table actions $app->modules->processDatalog(); - // Restart services that need to after configuration - $app->services->processDelayedActions(); - // All modules are already loaded and processed, so there is NO NEED to load the core once again... - $needStartCore = false; } + //** Process actions from sys_remoteaction table + $app->modules->processActions(); + //** Restart services that need to after configuration + $app->services->processDelayedActions(); + //** All modules are already loaded and processed, so there is NO NEED to load the core once again... + $needStartCore = false; + } else { if ($app->db->connect->connect_error == NULL) { $app->log('Unable to connect to local server.' . $app->db->errorMessage, LOGLEVEL_WARN); -- Gitblit v1.9.1