diff --git a/phpgwapi/cron/asyncservices.php b/phpgwapi/cron/asyncservices.php
index 95d5d7e4e0..c89ae78a36 100644
--- a/phpgwapi/cron/asyncservices.php
+++ b/phpgwapi/cron/asyncservices.php
@@ -24,19 +24,27 @@
/* $Id$ */
$path_to_phpgroupware = '../..'; // need to be adapted if this script is moved somewhere else
- $GLOBALS['domain'] = 'default';
+ $_GET['domain'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : 'default';
$GLOBALS['phpgw_info']['flags'] = array(
- 'currentapp' => 'login'
+ 'currentapp' => 'login',
+ 'noapi' => True // this stops header.inc.php to include phpgwapi/inc/function.inc.php
);
include($path_to_phpgroupware.'/header.inc.php');
+ unset($GLOBALS['phpgw_info']['flags']['noapi']);
- $num = ExecMethod('phpgwapi.asyncservice.check_run');
+ $db_type = $GLOBALS['phpgw_domain'][$_GET['domain']]['db_type'];
+ if (!extension_loaded($db_type) && !dl($db_type.'.so'))
+ {
+ echo "Extension '$db_type' is not loaded and can't be loaded via dl('$db_type.so') !!!\n";
+ }
+ $GLOBALS['phpgw_info']['server']['sessions_type'] = 'db';
+
+ include(PHPGW_API_INC.'/functions.inc.php');
+
+ $num = ExecMethod('phpgwapi.asyncservice.check_run','crontab');
// if the following comment got removed, you will get an email from cron for every check performed
- //echo date('Y/m/d H:i:s ').($num ? "$num job(s) executed" : 'Nothing to execute')."\n";
+ //echo date('Y/m/d H:i:s ').$_GET['domain'].': '.($num ? "$num job(s) executed" : 'Nothing to execute')."\n";
$GLOBALS['phpgw']->common->phpgw_exit();
-
-
-
diff --git a/phpgwapi/inc/class.asyncservice.inc.php b/phpgwapi/inc/class.asyncservice.inc.php
index b00c48726e..3bb8432c2d 100644
--- a/phpgwapi/inc/class.asyncservice.inc.php
+++ b/phpgwapi/inc/class.asyncservice.inc.php
@@ -7,21 +7,21 @@
* This library is part of the phpGroupWare API *
* http://www.phpgroupware.org/ *
* ------------------------------------------------------------------------ *
- * This library is free software; you can redistribute it and/or modify it *
- * under the terms of the GNU Lesser General Public License as published by *
- * the Free Software Foundation; either version 2.1 of the License, *
- * or any later version. *
- * This library is distributed in the hope that it will be useful, but *
- * WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
- * See the GNU Lesser General Public License for more details. *
- * You should have received a copy of the GNU Lesser General Public License *
- * along with this library; if not, write to the Free Software Foundation, *
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU General Public License as published by the *
+ * Free Software Foundation; either version 2 of the License, or (at your *
+ * option) any later version. *
\**************************************************************************/
/* $Id$ */
+ /*!
+ @class asyncservice
+ @author Ralf Becker
+ @copyright GPL - GNU General Public License
+ @abstract The class implements a general phpGW service to execute callbacks at a given time.
+ @discussion see http://www.phpgroupware.org/wiki/TimedAsyncServices
+ */
class asyncservice
{
var $public_functions = array(
@@ -31,47 +31,62 @@
'read' => True,
'install' => True,
'installed' => True,
- 'test' => True
+ 'last_check_run' => True
);
var $php = '';
var $crontab = '';
var $db;
var $db_table = 'phpgw_async';
+ var $debug = 0;
+ /*!
+ @function asyncservice
+ @abstract constructor of the class
+ */
function asyncservice()
{
$this->db = $GLOBALS['phpgw']->db;
+
+ $this->cronline = PHPGW_SERVER_ROOT . '/phpgwapi/cron/asyncservices.php '.$GLOBALS['phpgw_info']['user']['domain'];
+
+ $this->only_fallback = substr(php_uname(), 0, 7) == "Windows"; // atm cron-jobs dont work on win
}
/*!
@function set_timer
- @abstract calculates the next run of the timer and puts that with the rest of the data in the db
- @syntax set_timer($times,$id,$method,$data)
- @param $times unix timestamp or array('min','hour','dow','day','month','year') with execution time. \
- Repeated events are possible to shedule by setting the array only partly, eg. \
- array('day' => 1) for first day in each month 0am or array('min' => '* /5', 'hour' => '9-17') \
+ @abstract calculates the next run of the timer and puts that with the rest of the data in the db for later execution.
+ @syntax set_timer($times,$id,$method,$data,$account_id=False)
+ @param $times unix timestamp or array('min','hour','dow','day','month','year') with execution time.
+ Repeated events are possible to shedule by setting the array only partly, eg.
+ array('day' => 1) for first day in each month 0am or array('min' => '* /5', 'hour' => '9-17')
for every 5mins in the time from 9am to 5pm.
- @param $id unique id to cancel the request later, if necessary. Should be in a form like \
+ @param $id unique id to cancel the request later, if necessary. Should be in a form like
eg. ' next_run("; print_r($times); ",'$debug') $u: isset(times[$u]="; print_r($times[$u]); echo ")=".(isset($times[$u])?'True':'False')." n=$n, $u: isset(times[$u]="; print_r($times[$u]); echo ")=".(isset($times[$u])?'True':'False')." Nothing found, exiting !!! Nothing found, exiting !!! Have to try the next $next, $u's are over for $next=$over !!! Have to try the next $next, $u's are over for $next=$over !!! next="; print_r($found); echo " next="; print_r($found); echo " last_check_run(semaphore=".($semaphore?'True':'False').",release=".($release?'True':'False').") An other instance is running !!! $name = '".$this->$name."' Error: $this->crontab not found !!! Installing: '$cronline' Error: /usr/bin/crontab not found !!! async::next_run(";print_r($times);echo")=".($next === False ? 'False':"'$next'=".date('D(w) d.m.Y H:i',$next))." Error setting timer, maybe there's one already running !!! Error canceling timer, maybe there's none set !!! Installing: '$install[cronline]' Error: $async->crontab not found or other error !!!"; print_r($times); echo "
\n";
+ if ($this->debug) { echo "enumerated times="; print_r($times); echo "
\n"; }
// now we have the times enumerated, lets find the first not expired one
//
$found = array();
while (!isset($found['min']))
{
- $found = array();
$future = False;
- $n = 0;
foreach($units as $u => $date_pattern)
{
$unit_now = $u != 'dow' ? intval(date($date_pattern)) :
intval(date($date_pattern,mktime(12,0,0,$found['month'],$found['day'],$found['year'])));
+ if (isset($found[$u]))
+ {
+ $future = $future || $found[$u] > $unit_now;
+ if ($this->debug) echo "--> already have a $u = ".$found[$u].", future='$future'
\n";
+ continue; // already set
+ }
foreach($times[$u] as $unit_value => $nul)
{
switch($u)
@@ -267,21 +294,21 @@
if (!isset($found[$u])) // we have to try the next one, if it exists
{
$next = array_keys($units);
-
- if (!isset($next[$n-1]))
+ if (!isset($next[count($found)-1]))
{
- //echo ""; print_r($last_run); echo "
\n";
+
+ if (!$semaphore)
+ {
+ return $last_run['data'];
+ }
+ elseif (!$release && !$last_run['data']['end'] && $last_run['data']['start'] > time()-600)
+ {
+ // already one instance running (started not more then 10min ago, else we ignore it)
+
+ $this->db->unlock(); // unlock the table again
+
+ //echo ""; print_r($last_run); echo "
\n";
+ $this->write($last_run,!!$exits);
+
+ $this->db->unlock();
+
+ return True;
+ }
+
/*!
@function check_run
@abstract checks if there are any jobs ready to run (timer expired) and executes them
*/
- function check_run()
+ function check_run($run_by='')
{
- if (!($jobs = $this->read()))
+ flush();
+
+ if (!$this->last_check_run(True,False,$run_by))
{
- return False;
+ return False; // cant obtain semaphore
}
- foreach($jobs as $id => $job)
+ if ($jobs = $this->read())
{
- ExecMethod($job['method'],$job['data']);
-
- if ($job['next'] = $this->next_run($job['times']))
+ foreach($jobs as $id => $job)
{
- $this->write($job,True);
- }
- else // no further runs
- {
- $this->delete($job['id']);
+ // checking / setting up phpgw_info/user
+ //
+ if ($GLOBALS['phpgw_info']['user']['account_id'] != $job['account_id'])
+ {
+ $domain = $GLOBALS['phpgw_info']['user']['domain'];
+ $lang = $GLOBALS['phpgw_info']['user']['preferences']['common']['lang'];
+ unset($GLOBALS['phpgw_info']['user']);
+
+ if ($GLOBALS['phpgw']->session->account_id = $job['account_id'])
+ {
+ $GLOBALS['phpgw']->session->account_lid = $GLOBALS['phpgw']->accounts->id2name($job['account_id']);
+ $GLOBALS['phpgw']->session->account_domain = $domain;
+ $GLOBALS['phpgw']->session->read_repositories(False,False);
+ $GLOBALS['phpgw_info']['user'] = $GLOBALS['phpgw']->session->user;
+
+ if ($lang != $GLOBALS['phpgw_info']['user']['preferences']['common']['lang'])
+ {
+ unset($GLOBALS['lang']);
+ }
+ }
+ else
+ {
+ $GLOBALS['phpgw_info']['user']['domain'] = $domain;
+ }
+ }
+ list($app) = explode('.',$job['method']);
+ $GLOBALS['phpgw']->translation->add_app($app);
+
+ ExecMethod($job['method'],$job['data']);
+
+ if ($job['next'] = $this->next_run($job['times']))
+ {
+ $this->write($job,True);
+ }
+ else // no further runs
+ {
+ $this->delete($job['id']);
+ }
}
}
- return count($jobs);
+ $this->last_check_run(True,True,$run_by); // release semaphore
+
+ return $jobs ? count($jobs) : False;
}
/*!
@@ -329,18 +456,18 @@
@syntax reay($id=0)
@param $id =0 reads all expired rows / jobs ready to run\
!= 0 reads all rows/jobs matching $id (sql-wildcards '%' and '_' can be used)
- @returns db-rows / jobs as array or False if no matches
+ @result db-rows / jobs as array or False if no matches
*/
function read($id=0)
{
$id = $this->db->db_addslashes($id);
if (strpos($id,'%') !== False || strpos($id,'_') !== False)
{
- $where = "id LIKE '$id'";
+ $where = "id LIKE '$id' AND id!='##last-check-run##'";
}
elseif (!$id)
{
- $where = 'next<='.time();
+ $where = 'next<='.time()." AND id!='##last-check-run##'";
}
else
{
@@ -358,8 +485,10 @@
'next' => $this->db->f('next'),
'times' => unserialize($this->db->f('times')),
'method' => $this->db->f('method'),
- 'data' => unserialize($this->db->f('data'))
+ 'data' => unserialize($this->db->f('data')),
+ 'account_id' => $this->db->f('account_id')
);
+ //echo "job id='$id'"; print_r($jobs[$id]); echo "
\n";
}
if (!count($jobs))
{
@@ -379,23 +508,25 @@
{
$job['times'] = $this->db->db_addslashes(serialize($job['times']));
$job['data'] = $this->db->db_addslashes(serialize($job['data']));
+ $job['next'] = intval($job['next']);
+ $job['account_id'] = intval($job['account_id']);
if ($exists || $this->read($job['id']))
{
$this->db->query("UPDATE $this->db_table SET next=$job[next],times='$job[times]',".
- "method='$job[method]',data='$job[data]' WHERE id='$job[id]'",__FILE__,__LINE__);
+ "method='$job[method]',data='$job[data]',account_id=$job[account_id] WHERE id='$job[id]'",__LINE__,__FILE__);
}
else
{
- $this->db->query("INSERT INTO $this->db_table (id,next,times,method,data) VALUES ".
- "('$job[id]',$job[next],'$job[times]','$job[method]','$job[data]')",__FILE__,__LINE__);
+ $this->db->query("INSERT INTO $this->db_table (id,next,times,method,data,account_id) VALUES ".
+ "('$job[id]',$job[next],'$job[times]','$job[method]','$job[data]',$job[account_id])",__LINE__,__FILE__);
}
}
/*!
@function delete
@abstract delete db-row / job with $id
- @returns False if $id not found else True
+ @result False if $id not found else True
*/
function delete($id)
{
@@ -419,7 +550,11 @@
}
else
{
- $binarys = array('php' => '/usr/bin/php','crontab' => '/usr/bin/crontab');
+ $binarys = array(
+ 'php' => '/usr/bin/php',
+ 'php4' => '/usr/bin/php4', // this is for debian
+ 'crontab' => '/usr/bin/crontab'
+ );
foreach ($binarys as $name => $path)
{
$this->$name = $path; // a reasonable default for *nix
@@ -435,8 +570,16 @@
$this->$name = substr($this->$name,0,$pos);
}
}
+ if (!is_executable($this->$name))
+ {
+ $this->$name = $name; // hopefully its in the path
+ }
//echo "
\n";
+ $parts = split(' ',$line,6);
+
+ if ($line[0] == '#' || count($parts) < 6 || ($parts[5][0] != '/' && substr($parts[5],0,3) != 'php'))
+ {
+ // ignore comments
+ if ($line[0] != '#')
+ {
+ $times['error'] .= $line;
+ }
+ }
+ elseif (strstr($line,$this->cronline) !== False)
{
- $time = explode(' ',$line);
$cron_units = array('min','hour','day','month','dow');
foreach($cron_units as $n => $u)
{
- if ($time[$n] != '*')
- {
- $times[$u] = $time[$n];
- }
+ $times[$u] = $parts[$n];
}
$times['cronline'] = $line;
- break;
+ }
+ else
+ {
+ $this->other_cronlines[] = $line;
}
}
@pclose($crontab);
@@ -485,15 +644,15 @@
@abstract installs /phpgwapi/cron/asyncservices.php as cron-job
@syntax install($times)
@param $times array with keys 'min','hour','day','month','dow', not set is equal to '*'
- @returns the times asyncservices are run or False if they are not installed
- @note Not implemented for Windows at the moment, always dies with an error-message
+ @result the times asyncservices are run or False if they are not installed or 0 if crontab not found
+ @note Not implemented for Windows at the moment, always returns 0
*/
function install($times)
{
- if (substr(php_uname(), 0, 7) == "Windows") {
- die ("Sorry, no automatic on Windows at the moment !!!\n");
+ if ($this->only_fallback) {
+ return 0;
}
- $this->find_binarys();
+ $this->installed(); // find other installed cronlines
if (($crontab = popen('/bin/sh -c "'.$this->crontab.' -" 2>&1','w')) !== False)
{
@@ -502,121 +661,16 @@
{
$cronline .= (isset($times[$cu]) ? $times[$cu] : '*') . ' ';
}
- $cronline .= $this->php.' -q '.PHPGW_SERVER_ROOT . '/phpgwapi/cron/asyncservices.php'."\n";
+ $cronline .= $this->php.' -q '.$this->cronline."\n";
//echo "
jobs:
\n"; - if ($jobs = $async->read('%')) - { - echo "Id | Next run | Times | Method | Data |
---|---|---|---|---|
$job[id] | ".date('Y/m/d H:i',$job['next'])." | "; - print_r($job['times']); - echo " | $job[method] | "; - print_r($job['data']); - echo " |
No jobs in the database !!!
\n"; - } - echo "\n"; -} diff --git a/phpgwapi/inc/class.common.inc.php b/phpgwapi/inc/class.common.inc.php index 5b5f7267b0..d220b29a46 100644 --- a/phpgwapi/inc/class.common.inc.php +++ b/phpgwapi/inc/class.common.inc.php @@ -1661,6 +1661,12 @@ if (!@is_file(PHPGW_SERVER_ROOT . '/phpgwapi/templates/' . $GLOBALS['phpgw_info' } $GLOBALS['phpgw']->xslttpl->pp(); } + // call the asyncservice check_run function if it is not explicitly set to cron-only + // + if (!$GLOBALS['phpgw_info']['server']['asyncservice']) // is default + { + ExecMethod('phpgwapi.asyncservice.check_run','fallback'); + } $GLOBALS['phpgw']->db->disconnect(); /* Clean up mcrypt */ diff --git a/phpgwapi/setup/setup.inc.php b/phpgwapi/setup/setup.inc.php index 7886db0b57..d2f17f9d8c 100755 --- a/phpgwapi/setup/setup.inc.php +++ b/phpgwapi/setup/setup.inc.php @@ -12,7 +12,7 @@ /* Basic information about this app */ $setup_info['phpgwapi']['name'] = 'phpgwapi'; - $setup_info['phpgwapi']['version'] = '0.9.15.013'; + $setup_info['phpgwapi']['version'] = '0.9.15.014'; $setup_info['phpgwapi']['versions']['current_header'] = '1.23'; $setup_info['phpgwapi']['enable'] = 3; $setup_info['phpgwapi']['app_order'] = 1; diff --git a/phpgwapi/setup/tables_update.inc.php b/phpgwapi/setup/tables_update.inc.php index 58c4e9516d..e56e4cad6b 100644 --- a/phpgwapi/setup/tables_update.inc.php +++ b/phpgwapi/setup/tables_update.inc.php @@ -71,6 +71,45 @@ return $GLOBALS['setup_info']['phpgwapi']['currentver']; } + $test[] = '0.9.14.004'; + function phpgwapi_upgrade0_9_14_004() + { + // this is the 0.9.15.001 update + $GLOBALS['phpgw_setup']->oProc->RenameTable('lang','phpgw_lang'); + $GLOBALS['phpgw_setup']->oProc->RenameTable('languages','phpgw_languages'); + + // 0.9.15.002 are already included in 0.9.14.002 + + $GLOBALS['setup_info']['phpgwapi']['currentver'] = '0.9.15.003'; + return $GLOBALS['setup_info']['phpgwapi']['currentver']; + } + + $test[] = '0.9.14.005'; + function phpgwapi_upgrade0_9_14_005() + { + // this is the 0.9.15.001 update + $GLOBALS['phpgw_setup']->oProc->RenameTable('lang','phpgw_lang'); + $GLOBALS['phpgw_setup']->oProc->RenameTable('languages','phpgw_languages'); + + // 0.9.15.002 are already included in 0.9.14.002 + + $GLOBALS['setup_info']['phpgwapi']['currentver'] = '0.9.15.003'; + return $GLOBALS['setup_info']['phpgwapi']['currentver']; + } + + $test[] = '0.9.14.006'; + function phpgwapi_upgrade0_9_14_006() + { + // this is the 0.9.15.001 update + $GLOBALS['phpgw_setup']->oProc->RenameTable('lang','phpgw_lang'); + $GLOBALS['phpgw_setup']->oProc->RenameTable('languages','phpgw_languages'); + + // 0.9.15.002 are already included in 0.9.14.002 + + $GLOBALS['setup_info']['phpgwapi']['currentver'] = '0.9.15.003'; + return $GLOBALS['setup_info']['phpgwapi']['currentver']; + } + $test[] = '0.9.14.500'; function phpgwapi_upgrade0_9_14_500() { @@ -142,7 +181,11 @@ $test[] = '0.9.14.506'; function phpgwapi_upgrade0_9_14_506() { - // 0.9.15.001-13 are already included in 0.9.14.506 + $GLOBALS['phpgw_setup']->oProc->AlterColumn('phpgw_vfs','content',array( + 'type' => 'text', + 'nullable' => True + )); + $GLOBALS['setup_info']['phpgwapi']['currentver'] = '0.9.15.013'; return $GLOBALS['setup_info']['phpgwapi']['currentver']; } @@ -150,7 +193,15 @@ $test[] = '0.9.14.507'; function phpgwapi_upgrade0_9_14_507() { - // 0.9.15.001-14 are already included in 0.9.14.507 + // 0.9.15.001-13 are already included in 0.9.14.507 + $GLOBALS['setup_info']['phpgwapi']['currentver'] = '0.9.15.013'; + return $GLOBALS['setup_info']['phpgwapi']['currentver']; + } + + $test[] = '0.9.14.508'; + function phpgwapi_upgrade0_9_14_508() + { + // 0.9.15.001-14 are already included in 0.9.14.508 $GLOBALS['setup_info']['phpgwapi']['currentver'] = '0.9.15.014'; return $GLOBALS['setup_info']['phpgwapi']['currentver']; } @@ -414,13 +465,14 @@ return $GLOBALS['setup_info']['phpgwapi']['currentver']; } - $test[] = '0.9.15.013'; - function phpgwapi_upgrade0_9_14_506() + function phpgwapi_upgrade0_9_15_013() { - $GLOBALS['phpgw_setup']->oProc->AlterColumn('phpgw_vfs','content',array( - 'type' => 'text', - 'nullable' => True + $GLOBALS['phpgw_setup']->oProc->AddColumn('phpgw_async','account_id',array( + 'type' => 'int', + 'precision' => '4', + 'nullable' => False, + 'default' => '0' ));