move hooks class to api and only cache hooks in instance cache (instead of database: filesystem scan takes ~4ms, cache ~0.2ms)

This commit is contained in:
Ralf Becker 2016-04-02 14:16:20 +00:00
parent 682fd42a26
commit adb748d6e7
11 changed files with 604 additions and 427 deletions

View File

@ -20,10 +20,10 @@ class admin_hooks
/**
* Functions callable via menuaction
*
* @var unknown_type
* @var array
*/
var $public_functions = array(
'ajax_register_all_hooks' => True,
'ajax_clear_cache' => True,
);
/**
@ -118,7 +118,7 @@ class admin_hooks
'id' => 'admin/clear_cache',
'no_lang' => true,
'link' => "javascript:egw.message('".lang('Clear cache and register hooks') . "<br />" .lang('Please wait...')."','info'); " .
"egw.json('admin.admin_hooks.ajax_register_all_hooks').sendRequest(true);"
"egw.json('admin.admin_hooks.ajax_clear_cache').sendRequest(true);"
);
}
@ -159,9 +159,9 @@ class admin_hooks
}
/**
* Register all hooks
* Clears instance cache (and thereby also refreshes hooks)
*/
function ajax_register_all_hooks()
function ajax_clear_cache()
{
if ($GLOBALS['egw']->acl->check('applications_acc',16,'admin'))
{
@ -169,8 +169,6 @@ class admin_hooks
}
Api\Cache::flush(Api\Cache::INSTANCE);
$GLOBALS['egw']->hooks->register_all_hooks();
Api\Image::invalidate();
if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited
@ -178,9 +176,9 @@ class admin_hooks
$GLOBALS['egw']->invalidate_session_cache(); // in case with cache the egw_info array in the session
}
// allow apps to hook into "Admin >> Clear cache and register hooks"
$GLOBALS['egw']->hooks->process('clear_cache', array(), true);
Api\Hooks::process('clear_cache', array(), true);
egw_json_response::get()->apply('egw.message', array(lang('Done'),'success'));
Api\Json\Response::get()->apply('egw.message', array(lang('Done'), 'success'));
}
/**

124
api/setup/setup.inc.php Executable file
View File

@ -0,0 +1,124 @@
<?php
/**
* EGroupware - API Setup
*
* @link http://www.egroupware.org
* @package api
* @subpackage setup
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/* Basic information about this app */
$setup_info['api']['name'] = 'api';
$setup_info['api']['title'] = 'EGroupware API';
$setup_info['api']['version'] = '14.3.907';
$setup_info['api']['versions']['current_header'] = '1.29';
$setup_info['api']['enable'] = 3;
$setup_info['api']['app_order'] = 1;
$setup_info['api']['license'] = 'GPL';
$setup_info['api']['maintainer'] = $setup_info['api']['author'] = array(
'name' => 'EGroupware coreteam',
'email' => 'egroupware-developers@lists.sourceforge.net',
);
/* The tables this app creates
$setup_info['api']['tables'][] = 'egw_config';
$setup_info['api']['tables'][] = 'egw_applications';
$setup_info['api']['tables'][] = 'egw_acl';
$setup_info['api']['tables'][] = 'egw_accounts';
$setup_info['api']['tables'][] = 'egw_preferences';
$setup_info['api']['tables'][] = 'egw_access_log';
$setup_info['api']['tables'][] = 'egw_languages';
$setup_info['api']['tables'][] = 'egw_lang';
$setup_info['api']['tables'][] = 'egw_nextid';
$setup_info['api']['tables'][] = 'egw_categories';
$setup_info['api']['tables'][] = 'egw_history_log';
$setup_info['api']['tables'][] = 'egw_async';
$setup_info['api']['tables'][] = 'egw_links';
$setup_info['api']['tables'][] = 'egw_addressbook';
$setup_info['api']['tables'][] = 'egw_addressbook_extra';
$setup_info['api']['tables'][] = 'egw_addressbook_lists';
$setup_info['api']['tables'][] = 'egw_addressbook2list';
$setup_info['api']['tables'][] = 'egw_sqlfs';
$setup_info['api']['tables'][] = 'egw_locks';
$setup_info['api']['tables'][] = 'egw_sqlfs_props';
$setup_info['api']['tables'][] = 'egw_customfields';
$setup_info['api']['tables'][] = 'egw_sharing';
*/
// hooks used by vfs_home_hooks to manage user- and group-directories for the new stream based VFS
$setup_info['api']['hooks']['addaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::addAccount', 'EGroupware\\Api\\Mail\\Hooks::addaccount');
$setup_info['api']['hooks']['deleteaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::deleteAccount', 'EGroupware\\Api\\Mail\\Hooks::deleteaccount');
$setup_info['api']['hooks']['editaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::editAccount', 'EGroupware\\Api\\Mail\\Hooks::addaccount');
$setup_info['api']['hooks']['addgroup'] = 'EGroupware\\Api\\Vfs\\Hooks::addGroup';
$setup_info['api']['hooks']['deletegroup'] = array('EGroupware\\Api\\Vfs\\Hooks::deleteGroup', 'EGroupware\\Api\\Mail\\Hooks::deletegroup');
$setup_info['api']['hooks']['editgroup'] = 'EGroupware\\Api\\Vfs\\Hooks::editGroup';
$setup_info['api']['hooks']['changepassword'] = 'EGroupware\\Api\\Mail\\Hooks::changepassword';
// installation checks
$setup_info['api']['check_install'] = array(
'' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
),
'pear.horde.org/Horde_Imap_Client' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '2.24.2',
),
'pear.horde.org/Horde_Nls' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '2.0.3',
),
'pear.horde.org/Horde_Mail' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '2.1.2',
),
'pear.horde.org/Horde_Smtp' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '1.3.0',
),
'pear.horde.org/Horde_ManageSieve' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '1.0.1',
),
// next 4 are required for TNEF support
'pear.horde.org/Horde_Compress' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '2.0.8',
),
'pear.horde.org/Horde_Icalendar' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '2.0.0',
),
'pear.horde.org/Horde_Mapi' => array(
'func' => 'pear_check',
'from' => 'EMailAdmin',
'version' => '1.0.0',
),
'bcmath' => array(
'func' => 'extension_check',
'from' => 'EMailAdmin',
),
);
// CalDAV / CardDAV Sync
$setup_info['groupdav']['name'] = 'groupdav';
$setup_info['groupdav']['version'] = '14.1';
$setup_info['groupdav']['enable'] = 2;
$setup_info['groupdav']['app_order'] = 1;
$setup_info['groupdav']['icon'] = 'groupdav';
$setup_info['groupdav']['icon_app'] = 'api';
$setup_info['groupdav']['author'] = $setup_info['groupdav']['maintainer'] = array(
'name' => 'Ralf Becker',
'email' => 'RalfBecker@outdoor-training.de'
);
$setup_info['groupdav']['license'] = 'GPL';
$setup_info['groupdav']['hooks']['preferences'] = 'EGroupware\\Api\\CalDAV\\Hooks::menus';
$setup_info['groupdav']['hooks']['settings'] = 'EGroupware\\Api\\CalDAV\\Hooks::settings';

265
api/src/Hooks.php Normal file
View File

@ -0,0 +1,265 @@
<?php
/**
* EGroupware API - Hooks
*
* @link http://www.egroupware.org
* @author Dan Kuykendall <seek3r@phpgroupware.org>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* Copyright (C) 2000, 2001 Dan Kuykendall
* New method hooks and docu are written by <RalfBecker@outdoor-training.de>
* @license http://opensource.org/licenses/lgpl-license.php LGPL - GNU Lesser General Public License
* @package api
* @version $Id$
*/
namespace EGroupware\Api;
// explicitly import old not namespaced api classes
use applications;
/**
* Allow applications to set and use hooks to communicate with each other
*
* Hooks need to be declared in the app's setup.inc.php file and
* are cached in instance cache for 1h.
*
* Clearing instance cache or calling Api\Hooks::read(true) forces a new scan.
*
* Hooks can have one of the following formats:
* - static class method hooks are declared as:
* $setup_info['appname']['hooks']['location'] = 'class::method';
* - method hooks, which are methods of a class. You can pass parameters to the call and
* they can return values. They get declared in setup.inc.php as:
* $setup_info['appname']['hooks']['location'] = 'app.class.method';
* - old type, which are included files. Values can only be passed by global values and they cant return anything.
* Old declaration in setup.inc.php:
* $setup_info['appname']['hooks'][] = 'location';
*/
class Hooks
{
/**
* Hooks by location and appname
*
* @var array $location => $app => array($file, ...)
*/
protected static $locations;
/**
* Executes all the hooks (the user has rights to) for a given location
*
* If no $order given, hooks are executed in the order of the applications!
*
* @param string|array $args location-name as string or array with keys location and
* further data to be passed to the hook, if its a new method-hook
* @param string|array $order appname(s as value), which should be executes first
* @param boolean $no_permission_check if True execute all hooks, not only the ones a user has rights to
* $no_permission_check should *ONLY* be used when it *HAS* to be. (jengo)
* @return array with results of each hook call (with appname as key) and value:
* - False if no hook exists (should no longer be the case),
* - True if old hook exists and
* - array of return-values, if an app implements more then one hook
* - whatever the new method-hook returns (can be True or False too!)
*/
public static function process($args, $order = array(), $no_permission_check = False)
{
//echo "<p>".__METHOD__.'('.array2string($args).','.array2string($order).','.array2string($no_permission_check).")</p>\n";
$location = is_array($args) ? (isset($args['hook_location']) ? $args['hook_location'] : $args['location']) : $args;
if (!isset(self::$locations)) self::read();
$hooks = self::$locations[$location];
if (!isset($hooks) || empty($hooks)) return array(); // not a single app implements that hook
$apps = array_keys($hooks);
if (!$no_permission_check)
{
// on install of a new egroupware both hook-apps and user apps may be empty/not set
$apps = array_intersect((array)$apps,array_keys((array)$GLOBALS['egw_info']['user']['apps']));
}
if ($order)
{
$apps = array_unique(array_merge((array)$order,$apps));
}
$results = array();
foreach((array)$apps as $appname)
{
$results[$appname] = self::single($args,$appname,$no_permission_check);
}
return $results;
}
/**
* Executes a single hook of a given location and application
*
* @param string|array $args location-name as string or array with keys location, appname and
* further data to be passed to the hook, if its a new method-hook
* @param string $appname name of the app, which's hook to execute, if empty the current app is used
* @param boolean $no_permission_check =false if True execute all hooks, not only the ones a user has rights to
* $no_permission_check should *ONLY* be used when it *HAS* to be. (jengo)
* @param boolean $try_unregistered =false If true, try to include old file-hook anyway (for setup)
* @return mixed False if no hook exists, True if old hook exists and whatever the new method-hook returns (can be True or False too!).
*/
public static function single($args, $appname = '', $no_permission_check = False)//,$try_unregistered = False)
{
//error_log(__METHOD__."(".array2string($args).",'$appname','$no_permission_check','$try_unregistered')");
if (!isset(self::$locations)) self::read();
if (!is_array($args)) $args = array('location' => $args);
$location = isset($args['hook_location']) ? $args['hook_location'] : $args['location'];
if (!$appname)
{
$appname = is_array($args) && isset($args['appname']) ? $args['appname'] : $GLOBALS['egw_info']['flags']['currentapp'];
}
// excute hook only if $no_permission_check or user has run-rights for app
if (!($no_permission_check || isset($GLOBALS['egw_info']['user']['apps'][$appname])))
{
return false;
}
$ret = array();
foreach((array)self::$locations[$location][$appname] as $hook)
{
try {
// old style file hook
if ($hook[0] == '/')
{
if (!file_exists(EGW_SERVER_ROOT.$hook))
{
error_log(__METHOD__."() old style hook file '$hook' not found --> ignored!");
continue;
}
include(EGW_SERVER_ROOT.$hook);
return true;
}
list($class, $method) = explode('::', $hook);
// static method of an autoloadable class
if (isset($method) && class_exists($class))
{
if (is_callable($hook)) $ret[] = call_user_func($hook, $args);
}
// app.class.method or not autoloadable class
else
{
$ret[] = ExecMethod2($hook, $args);
}
}
catch (\Exception $e) {
_egw_log_exception($e);
}
}
if (!$ret) return false;
return count($ret) == 1 ? $ret[0] : $ret;
}
/**
* loop through the applications and count the apps implementing a hooks
*
* @param string $location location-name
* @return int the number of found hooks
*/
function count($location)
{
if (!isset(self::$locations)) self::read();
return count(self::$locations[$location]);
}
/**
* check if a given hook for an application is registered
*
* @param string $location location-name
* @param string $app appname
* @param boolean $return_methods =false true: return hook-method(s)
* @return int|array the number of found hooks or for $return_methods array with methods
*/
public static function exists($location, $app, $return_methods=false)
{
if (!isset(self::$locations)) self::read();
//error_log(__METHOD__.__LINE__.array2string(self::$locations[$location]));
return $return_methods ? self::$locations[$location][$app] : count(self::$locations[$location][$app]);
}
/**
* check which apps implement a given hook
*
* @param string $location location-name
* @return array of apps implementing given hook
*/
public static function implemented($location)
{
if (!isset(self::$locations)) self::read();
//error_log(__METHOD__.__LINE__.array2string(self::$locations[$location]));
return isset(self::$locations[$location]) ? array_keys(self::$locations[$location]) : array();
}
/**
* Read all hooks into self::$locations
*
* @param boolan $force_rescan =false true: do not use instance cache
*/
protected static function read($force_rescan=false)
{
//$starttime = microtime(true);
if ($force_rescan) Cache::unsetInstance(__CLASS__, 'locations');
self::$locations = Cache::getInstance(__CLASS__, 'locations', function()
{
// if we run in setup, we need to read installed apps first
if (!$GLOBALS['egw_info']['apps'])
{
$applications = new applications();
$applications->read_installed_apps();
}
// read all apps using just filesystem data
$locations = array();
foreach(array_merge(array('api'), array_keys($GLOBALS['egw_info']['apps'])) as $appname)
{
if ($appname[0] == '.' || !is_dir(EGW_SERVER_ROOT.'/'.$appname)) continue;
$f = EGW_SERVER_ROOT . '/' . $appname . '/setup/setup.inc.php';
$setup_info = array($appname => array());
if(@file_exists($f)) include($f);
// some apps have setup_info for more then themselfs (eg. api for groupdav)
foreach($setup_info as $appname => $data)
{
foreach((array)$data['hooks'] as $location => $methods)
{
if (is_int($location))
{
$location = $methods;
$methods = '/'.$appname.'/inc/hook_'.$methods.'.inc.php';
}
$locations[$location][$appname] = (array)$methods;
}
}
}
return $locations;
}, array(), 3600);
//error_log(__METHOD__."() took ".number_format(1000*(microtime(true)-$starttime), 1)."ms, size=".Vfs::hsize(strlen(json_encode(self::$locations))));
}
/**
* Static function to build pgp encryption sidebox menu
* @param type $appname application name
*/
public static function pgp_encryption_menu($appname)
{
if (Header\UserAgent::mobile()) return;
// PGP Encryption (Mailvelope plugin) restore/backup menu
$file = Array(
'Backup/Restore ...' => 'javascript:app.'.$appname.'.mailvelopeCreateBackupRestoreDialog();',
);
display_sidebox($appname, lang('PGP Encryption'), $file);
}
}

109
api/src/Mail/Hooks.php Normal file
View File

@ -0,0 +1,109 @@
<?php
/**
* EGroupware - Mail hooks
*
* @link http://www.egroupware.org
* @package api
* @subpackage amil
* @author Klaus Leithoff <leithoff-AT-stylite.de>
* @author Ralf Becker <rb@stylite.de>
* @copyright (c) 2008-16 by leithoff-At-stylite.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
namespace EGroupware\Api\Mail;
/**
* diverse static Mail hooks
*/
class Hooks
{
/**
* Password changed hook --> unset cached objects, as password might be used for email connection
*
* @param array $hook_data
*/
public static function changepassword($hook_data)
{
if (!empty($hook_data['old_passwd']))
{
Credentials::changepassword($hook_data);
}
}
/**
* Hook called before an account get deleted
*
* @param array $data
* @param int $data['account_id'] numerical id
* @param string $data['account_lid'] account-name
* @param int $data['new_owner'] account-id of new owner, or false if data should get deleted
*/
static function deleteaccount(array $data)
{
self::run_plugin_hooks('deleteAccount', $data);
// as mail accounts contain credentials, we do NOT assign them to user users
Account::delete(0, $data['account_id']);
}
/**
* Hook called before a group get deleted
*
* @param array $data
* @param int $data['account_id'] numerical id
* @param string $data['account_name'] account-name
*/
static function deletegroup(array $data)
{
Account::delete(0, $data['account_id']);
}
/**
* Hook called when an account get added or edited
*
* @param array $data
* @param int $data['account_id'] numerical id
* @param string $data['account_lid'] account-name
* @param string $data['account_email'] email
*/
static function addaccount(array $data)
{
$method = $data['location'] == 'addaccount' ? 'addAccount' : 'updateAccount';
self::run_plugin_hooks($method, $data);
}
/**
* Run hook on plugins of all mail-accounts of given account_id
*
* @param string $method plugin method to run
* @param array $data hook-data incl. value for key account_id
*/
protected static function run_plugin_hooks($method, array $data)
{
foreach(Account::search((int)$data['account_id'], 'params') as $params)
{
if (!Account::is_multiple($params)) continue; // no need to waste time on personal accounts
try {
$account = new Account($params);
if ($account->acc_smtp_type != 'emailadmin_smtp' && ($smtp = $account->smtpServer(true)) &&
is_a($smtp, 'emailadmin_smtp') && get_class($smtp) != 'emailadmin_smtp')
{
$smtp->$method($data);
}
if ($account->acc_imap_type != 'emailadmin_imap' && $account->acc_imap_admin_username &&
$account->acc_imap_admin_password && ($imap = $account->imapServer(true)) &&
is_a($imap, 'emailadmin_imap') && get_class($imap) != 'emailadmin_imap')
{
$imap->$method($data);
}
}
catch(\Exception $e) {
_egw_log_exception($e);
// ignore exception, without stalling other hooks
}
}
}
}

View File

@ -1,18 +1,22 @@
<?php
/**
* eGroupWare API: VFS - Hooks to add/rename/delete user and group home-directories
* EGroup2are API: VFS - Hooks to add/rename/delete user and group home-directories
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
namespace EGroupware\Api\Vfs;
use EGroupware\Api;
/**
* eGroupWare API: VFS - Hooks to add/rename/delete user and group home-directories
* VFS - Hooks to add/rename/delete user and group home-directories
*
* This class implements the creation, renaming or deletion of home-dirs via some hooks from admin:
* - create the homedir if a new user gets created
@ -20,7 +24,7 @@
* - delete the homedir or copy its content to an other users homedir, if a user gets deleted
* --> these hooks are registered via phpgwapi/setup/setup.inc.php and called by the admin app
*/
class vfs_home_hooks
class Hooks
{
/**
* Should we log our calls to the error_log
@ -40,14 +44,14 @@ class vfs_home_hooks
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($data).')');
// create a user-dir
egw_vfs::$is_root = true;
if (egw_vfs::mkdir($dir='/home/'.$data['account_lid'],0700,0))
Api\Vfs::$is_root = true;
if (Api\Vfs::mkdir($dir='/home/'.$data['account_lid'],0700,0))
{
egw_vfs::chown($dir,$data['account_id']);
egw_vfs::chgrp($dir,0);
egw_vfs::chmod($dir,0700); // only user has access
Api\Vfs::chown($dir,$data['account_id']);
Api\Vfs::chgrp($dir,0);
Api\Vfs::chmod($dir,0700); // only user has access
}
egw_vfs::$is_root = false;
Api\Vfs::$is_root = false;
}
/**
@ -66,9 +70,9 @@ class vfs_home_hooks
return; // nothing to do here
}
// rename the user-dir
egw_vfs::$is_root = true;
egw_vfs::rename('/home/'.$data['old_loginid'],'/home/'.$data['account_lid']);
egw_vfs::$is_root = false;
Api\Vfs::$is_root = true;
Api\Vfs::rename('/home/'.$data['old_loginid'],'/home/'.$data['account_lid']);
Api\Vfs::$is_root = false;
}
/**
@ -82,25 +86,28 @@ class vfs_home_hooks
static function deleteAccount($data)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($data).')');
egw_vfs::$is_root = true;
Api\Vfs::$is_root = true;
if ($data['new_owner'] && ($new_lid = $GLOBALS['egw']->accounts->id2name($data['new_owner'])))
{
// copy content of user-dir to new owner's user-dir as old-home-$name
for ($i=''; file_exists(egw_vfs::PREFIX.($new_dir = '/home/'.$new_lid.'/old-home-'.$data['account_lid'].$i)); $i++);
egw_vfs::rename('/home/'.$data['account_lid'],$new_dir);
for ($i=''; file_exists(Api\Vfs::PREFIX.($new_dir = '/home/'.$new_lid.'/old-home-'.$data['account_lid'].$i)); $i++)
{
}
Api\Vfs::rename('/home/'.$data['account_lid'],$new_dir);
// make the new owner the owner of the dir and it's content
egw_vfs::find($new_dir,array(),array('egw_vfs','chown'),$data['new_owner']);
Api\Vfs::find($new_dir,array(),array('egw_vfs','chown'),$data['new_owner']);
}
elseif(!empty($data['account_lid']) && $data['account_lid'] != '/')
{
// delete the user-directory
egw_vfs::remove('/home/'.$data['account_lid']);
Api\Vfs::remove('/home/'.$data['account_lid']);
}
else
{
throw new egw_exception_assertion_failed(__METHOD__.'('.array2string($data).') account_lid NOT set!');
throw new Api\Exception\AssertionFailed(__METHOD__.'('.array2string($data).') account_lid NOT set!');
}
egw_vfs::$is_root = false;
Api\Vfs::$is_root = false;
}
/**
@ -113,17 +120,17 @@ class vfs_home_hooks
static function addGroup($data)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($data).')');
if (empty($data['account_lid'])) throw new egw_exception_wrong_parameter('account_lid must not be empty!');
if (empty($data['account_lid'])) throw new Api\Exception\WrongParameter('account_lid must not be empty!');
// create a group-dir
egw_vfs::$is_root = true;
if (egw_vfs::mkdir($dir='/home/'.$data['account_lid'],0070,0))
Api\Vfs::$is_root = true;
if (Api\Vfs::mkdir($dir='/home/'.$data['account_lid'],0070,0))
{
egw_vfs::chown($dir,0);
egw_vfs::chgrp($dir,abs($data['account_id'])); // gid in Vfs is positiv!
egw_vfs::chmod($dir,0070); // only group has access
Api\Vfs::chown($dir,0);
Api\Vfs::chgrp($dir,abs($data['account_id'])); // gid in Vfs is positiv!
Api\Vfs::chmod($dir,0070); // only group has access
}
egw_vfs::$is_root = false;
Api\Vfs::$is_root = false;
}
/**
@ -145,7 +152,7 @@ class vfs_home_hooks
if ($data['account_lid'] == $data['old_name'])
{
// check if group directory exists and create it if not (by calling addGroup hook)
if (!egw_vfs::stat('/home/'.$data['account_lid']))
if (!Api\Vfs::stat('/home/'.$data['account_lid']))
{
self::addGroup($data);
}
@ -153,9 +160,9 @@ class vfs_home_hooks
else
{
// rename the group-dir
egw_vfs::$is_root = true;
egw_vfs::rename('/home/'.$data['old_name'],'/home/'.$data['account_lid']);
egw_vfs::$is_root = false;
Api\Vfs::$is_root = true;
Api\Vfs::rename('/home/'.$data['old_name'],'/home/'.$data['account_lid']);
Api\Vfs::$is_root = false;
}
}
@ -172,11 +179,11 @@ class vfs_home_hooks
if(empty($data['account_lid']) || $data['account_lid'] == '/')
{
throw new egw_exception_assertion_failed(__METHOD__.'('.array2string($data).') account_lid NOT set!');
throw new Api\Exception\AssertionFailed(__METHOD__.'('.array2string($data).') account_lid NOT set!');
}
// delete the group-directory
egw_vfs::$is_root = true;
egw_vfs::remove('/home/'.$data['account_lid']);
egw_vfs::$is_root = false;
Api\Vfs::$is_root = true;
Api\Vfs::remove('/home/'.$data['account_lid']);
Api\Vfs::$is_root = false;
}
}

View File

@ -12,199 +12,38 @@
* @version $Id$
*/
use EGroupware\Api;
/**
* class which gives ability for applications to set and use hooks to communicate with each other
*
* Hooks need to be declared in the app's setup.inc.php file and they have to be registered
* (copied into the database) by
* - installing or updating the app via setup or
* - running Admin >> register all hooks
* As the hooks-class can get cached in the session (session-type PHP_RESTORE), you also have to log
* out and in again, that your changes take effect.
*
* Hooks can have one of the following formats:
* - static class method hooks are declared as:
* $setup_info['appname']['hooks']['location'] = 'class::method';
* - method hooks, which are methods of a class. You can pass parameters to the call and
* they can return values. They get declared in setup.inc.php as:
* $setup_info['appname']['hooks']['location'] = 'app.class.method';
* - old type, which are included files. Values can only be passed by global values and they cant return anything.
* Old declaration in setup.inc.php:
* $setup_info['appname']['hooks'][] = 'location';
* @deprecated use static methods of Api\Hooks::process() and Api\Hooks::single
*/
class hooks
class hooks extends Api\Hooks
{
/**
* Reference to the global db object
*
* @var egw_db
*/
var $db;
var $table = 'egw_hooks';
/**
* Hooks by location and appname
*
* @var array $location => $app => $file
*/
var $locations;
/**
* constructor, reads and caches the complete hooks table
*
* @param egw_db $db =null database class, if null we use $GLOBALS['egw']->db
*/
function __construct($db=null)
{
$this->db = $db ? $db : $GLOBALS['egw']->db; // this is to allow setup to set the db
// sort hooks by app-order
foreach($this->db->select($this->table,'hook_appname,hook_location,hook_filename',false,__LINE__,__FILE__,false,'ORDER BY app_order','phpgwapi',0,'JOIN egw_applications ON hook_appname=app_name') as $row)
{
$this->locations[$row['hook_location']][$row['hook_appname']] = $row['hook_filename'];
}
//_debug_array($this->locations);
}
/**
* Executes all the hooks (the user has rights to) for a given location
*
* If no $order given, hooks are executed in the order of the applications!
*
* @param string|array $args location-name as string or array with keys location and
* further data to be passed to the hook, if its a new method-hook
* @param string|array $order appname(s as value), which should be executes first
* @param boolean $no_permission_check if True execute all hooks, not only the ones a user has rights to
* $no_permission_check should *ONLY* be used when it *HAS* to be. (jengo)
* @return array with results of each hook call (with appname as key) and value:
* - False if no hook exists (should no longer be the case),
* - True if old hook exists and
* - whatever the new method-hook returns (can be True or False too!).
*/
function process($args, $order = array(), $no_permission_check = False)
{
//echo "<p>".__METHOD__.'('.array2string($args).','.array2string($order).','.array2string($no_permission_check).")</p>\n";
$location = is_array($args) ? (isset($args['hook_location']) ? $args['hook_location'] : $args['location']) : $args;
$hooks = $this->locations[$location];
if (!isset($hooks) || empty($hooks)) return array(); // not a single app implements that hook
$apps = array_keys($hooks);
if (!$no_permission_check)
{
// on install of a new egroupware both hook-apps and user apps may be empty/not set
$apps = array_intersect((array)$apps,array_keys((array)$GLOBALS['egw_info']['user']['apps']));
}
if ($order)
{
$apps = array_unique(array_merge((array)$order,$apps));
}
$results = array();
foreach((array)$apps as $appname)
{
$results[$appname] = $this->single($args,$appname,$no_permission_check);
}
return $results;
}
/**
* executes a single hook of a given location and application
*
* @param string|array $args location-name as string or array with keys location, appname and
* further data to be passed to the hook, if its a new method-hook
* @param string $appname name of the app, which's hook to execute, if empty the current app is used
* @param boolean $no_permission_check =false if True execute all hooks, not only the ones a user has rights to
* $no_permission_check should *ONLY* be used when it *HAS* to be. (jengo)
* @param boolean $try_unregistered =false If true, try to include old file-hook anyway (for setup)
* @return mixed False if no hook exists, True if old hook exists and whatever the new method-hook returns (can be True or False too!).
*/
function single($args, $appname = '', $no_permission_check = False,$try_unregistered = False)
{
//echo "<p>hooks::single(".array2string($args).",'$appname','$no_permission_check','$try_unregistered')</p>\n";
if (!is_array($args)) $args = array('location' => $args);
$location = isset($args['hook_location']) ? $args['hook_location'] : $args['location'];
if (!$appname)
{
$appname = is_array($args) && isset($args['appname']) ? $args['appname'] : $GLOBALS['egw_info']['flags']['currentapp'];
}
// excute hook only if $no_permission_check or user has run-rights for app
if (!($no_permission_check || isset($GLOBALS['egw_info']['user']['apps'][$appname])))
{
return false;
}
$SEP = filesystem_separator();
/* First include the ordered apps hook file */
if (isset($this->locations[$location][$appname]) || $try_unregistered)
{
$parts = explode('.',$method = $this->locations[$location][$appname]);
if (strpos($method,'::') !== false || count($parts) == 3 && $parts[1] != 'inc' && $parts[2] != 'php')
{
// new style hook with method string or static method (eg. 'class::method')
try
{
return ExecMethod($method,$args);
}
catch(egw_exception_assertion_failed $e)
{
if (substr($e->getMessage(),-19) == '.inc.php not found!')
{
return false; // fail gracefully if hook class-file does not exists (like the old hooks do, eg. if app got removed)
}
throw $e;
}
}
// old style hook, with an include file
if ($try_unregistered && empty($method))
{
$method = 'hook_'.$location.'.inc.php';
}
$f = EGW_SERVER_ROOT . $SEP . $appname . $SEP . 'inc' . $SEP . $method;
if (file_exists($f) &&
( $GLOBALS['egw_info']['user']['apps'][$appname] || (($no_permission_check || $location == 'config' || $appname == 'phpgwapi') && $appname)) )
{
include($f);
return True;
}
}
return False;
}
/**
* loop through the applications and count the hooks
*
* @param string $location location-name
* @return int the number of found hooks
*/
function count($location)
{
return count($this->locations[$location]);
}
/**
* check if a given hook for an application is registered
*
* @param string $location location-name
* @param string $app appname
* @deprecated use exists($location, $app)
* @return int the number of found hooks
*/
function hook_exists($location, $app)
public static function hook_exists($location, $app)
{
//error_log(__METHOD__.__LINE__.array2string($this->locations[$location]));
return count($this->locations[$location][$app]);
return self::exists($location, $app);
}
/**
* check which apps implement a given hook
*
* @param string $location location-name
* @deprecated use implemented($location)
* @return array of apps implementing given hook
*/
function hook_implemented($location)
public static function hook_implemented($location)
{
//error_log(__METHOD__.__LINE__.array2string($this->locations[$location]));
return isset($this->locations[$location]) ? array_keys($this->locations[$location]) : array();
return self::implemented($location);
}
/**
@ -214,42 +53,16 @@ class hooks
*
* @param string $appname Application 'name'
* @param array $hooks =null hooks to register, eg $setup_info[$app]['hooks'] or not used for only deregister the hooks
* @deprecated use Api\Hooks::read(true) to force rescan of hooks
* @return boolean|int false on error, true if new hooks are supplied and registed or number of removed hooks
*/
function register_hooks($appname,$hooks=null)
public static function register_hooks($appname,$hooks=null)
{
if(!$appname)
{
return False;
}
$this->db->delete($this->table,array('hook_appname' => $appname),__LINE__,__FILE__);
unset($appname, $hooks);
if (!is_array($hooks) || !count($hooks)) // only deregister
{
return $this->db->affected_rows();
}
//echo "<p>ADDING hooks for: $appname</p>";
foreach($hooks as $key => $hook)
{
if (!is_numeric($key)) // new method-hook
{
$location = $key;
$filename = $hook;
}
else
{
$location = $hook;
$filename = "hook_$hook.inc.php";
}
$this->db->insert($this->table,array(
'hook_filename' => $filename,
),array(
'hook_appname' => $appname,
'hook_location' => $location,
),__LINE__,__FILE__);
$this->locations[$location][$appname] = $filename;
}
return True;
self::read(true);
return true;
}
/**
@ -259,115 +72,24 @@ class hooks
*
* @param string $appname Application 'name'
* @param string $location is required, the hook itself
* @deprecated use Api\Hooks::read(true) to force rescan of hooks
* @return boolean|int false on error, true if new hooks are supplied and registed or number of removed hooks
*/
function register_single_app_hook($appname, $location)
public static function register_single_app_hook($appname, $location)
{
if(!$appname || empty($location))
{
return False;
}
$SEP = filesystem_separator();
// now register the rest again
$f = EGW_SERVER_ROOT . $SEP . $appname . $SEP . 'setup' . $SEP . 'setup.inc.php';
$setup_info = array($appname => array());
if(@file_exists($f)) include($f);
// some apps have setup_info for more then themselfs (eg. phpgwapi for groupdav)
$hdata = array();
foreach($setup_info as $appname => $data)
{
if ($data['hooks'])
{
if ($hdata[$appname])
{
$hdata[$appname]['hooks'] = array_merge($hdata[$appname]['hooks'],$data['hooks']);
}
else
{
$hdata[$appname]['hooks'] = $data['hooks'];
}
}
}
//error_log(__METHOD__.__LINE__.array2string($hdata));
foreach((array)$hdata as $appname => $data)
{
if (array_key_exists($location,$data['hooks'])) $method = $data['hooks'][$location];
}
if (!empty($method))
{
//echo "<p>ADDING hooks for: $appname</p>";
$this->db->insert($this->table,array(
'hook_appname' => $appname,
'hook_filename' => $method,
'hook_location' => $location,
),array(
'hook_appname' => $appname,
'hook_location' => $location,
),__LINE__,__FILE__);
$this->locations[$location][$appname] = $method;
return True;
}
return false;
self::read(true);
return !!self::exists($location, $appname);
}
/**
* Register the hooks of all applications (used by admin)
*
* @deprecated use Api\Hooks::read(true) to force rescan of hooks
*/
function register_all_hooks()
public static function register_all_hooks()
{
// deleting hooks, to get ride of no longer existing apps
$this->db->delete($this->table,'1=1',__LINE__,__FILE__);
// if we run in setup, we need to read installed apps first
if (!$GLOBALS['egw_info']['apps'])
{
$applications = new applications();
$applications->read_installed_apps();
}
// now register all apps using just filesystem data
foreach(array_keys($GLOBALS['egw_info']['apps']) as $appname)
{
if ($appname[0] == '.' || !is_dir(EGW_SERVER_ROOT.'/'.$appname)) continue;
$f = EGW_SERVER_ROOT . '/' . $appname . '/setup/setup.inc.php';
$setup_info = array($appname => array());
if(@file_exists($f)) include($f);
// some apps have setup_info for more then themselfs (eg. phpgwapi for groupdav)
$hdata = array();
foreach($setup_info as $appname => $data)
{
if ($data['hooks'])
{
if ($hdata[$appname])
{
$hdata[$appname]['hooks'] = array_merge($hdata[$appname]['hooks'],$data['hooks']);
}
else
{
$hdata[$appname]['hooks'] = $data['hooks'];
}
}
}
foreach((array)$hdata as $appname => $data)
{
if ($data['hooks']) $this->register_hooks($appname,$data['hooks']);
}
}
}
/**
* Static function to build pgp encryption sidebox menu
* @param type $appname application name
*/
public static function pgp_encryption_menu($appname)
{
if (html::$ua_mobile) return;
// PGP Encryption (Mailvelope plugin) restore/backup menu
$file = Array(
'Backup/Restore ...' => 'javascript:app.'.$appname.'.mailvelopeCreateBackupRestoreDialog();',
);
display_sidebox($appname, lang('PGP Encryption'), $file);
self::read(true);
}
/**

View File

@ -11,8 +11,8 @@
/* Basic information about this app */
$setup_info['phpgwapi']['name'] = 'phpgwapi';
$setup_info['phpgwapi']['title'] = 'EGroupware API';
$setup_info['phpgwapi']['version'] = '14.3.906';
$setup_info['phpgwapi']['title'] = 'EGroupware old API';
$setup_info['phpgwapi']['version'] = '14.3.908';
$setup_info['phpgwapi']['versions']['current_header'] = '1.29';
$setup_info['phpgwapi']['enable'] = 3;
$setup_info['phpgwapi']['app_order'] = 1;
@ -29,10 +29,8 @@ $setup_info['phpgwapi']['tables'][] = 'egw_acl';
$setup_info['phpgwapi']['tables'][] = 'egw_accounts';
$setup_info['phpgwapi']['tables'][] = 'egw_preferences';
$setup_info['phpgwapi']['tables'][] = 'egw_access_log';
$setup_info['phpgwapi']['tables'][] = 'egw_hooks';
$setup_info['phpgwapi']['tables'][] = 'egw_languages';
$setup_info['phpgwapi']['tables'][] = 'egw_lang';
$setup_info['phpgwapi']['tables'][] = 'egw_nextid';
$setup_info['phpgwapi']['tables'][] = 'egw_categories';
$setup_info['phpgwapi']['tables'][] = 'egw_history_log';
$setup_info['phpgwapi']['tables'][] = 'egw_async';
@ -49,28 +47,3 @@ $setup_info['phpgwapi']['tables'][] = 'egw_locks';
$setup_info['phpgwapi']['tables'][] = 'egw_sqlfs_props';
$setup_info['phpgwapi']['tables'][] = 'egw_customfields';
$setup_info['phpgwapi']['tables'][] = 'egw_sharing';
// hooks used by vfs_home_hooks to manage user- and group-directories for the new stream based VFS
$setup_info['phpgwapi']['hooks']['addaccount'] = 'phpgwapi.vfs_home_hooks.addAccount';
$setup_info['phpgwapi']['hooks']['deleteaccount'] = 'phpgwapi.vfs_home_hooks.deleteAccount';
$setup_info['phpgwapi']['hooks']['editaccount'] = 'phpgwapi.vfs_home_hooks.editAccount';
$setup_info['phpgwapi']['hooks']['addgroup'] = 'phpgwapi.vfs_home_hooks.addGroup';
$setup_info['phpgwapi']['hooks']['deletegroup'] = 'phpgwapi.vfs_home_hooks.deleteGroup';
$setup_info['phpgwapi']['hooks']['editgroup'] = 'phpgwapi.vfs_home_hooks.editGroup';
/* CalDAV/CardDAV/GroupDAV app */
$setup_info['groupdav']['name'] = 'groupdav';
$setup_info['groupdav']['version'] = '14.1';
$setup_info['groupdav']['enable'] = 2;
$setup_info['groupdav']['app_order'] = 1;
$setup_info['groupdav']['icon'] = 'groupdav';
$setup_info['groupdav']['icon_app'] = 'phpgwapi';
$setup_info['groupdav']['author'] = $setup_info['groupdav']['maintainer'] = array(
'name' => 'Ralf Becker',
'email' => 'RalfBecker@outdoor-training.de'
);
$setup_info['groupdav']['license'] = 'GPL';
$setup_info['groupdav']['hooks']['preferences'] = 'EGroupware\\Api\\CalDAV\\Hooks::menus';
$setup_info['groupdav']['hooks']['settings'] = 'EGroupware\\Api\\CalDAV\\Hooks::settings';

View File

@ -103,18 +103,6 @@ $phpgw_baseline = array(
'ix' => array('li','lo','session_dla','session_php','notification_heartbeat',array('account_id','ip','li'),array('account_id','loginid','li')),
'uc' => array()
),
'egw_hooks' => array(
'fd' => array(
'hook_id' => array('type' => 'auto','nullable' => False),
'hook_appname' => array('type' => 'ascii','precision' => '16'),
'hook_location' => array('type' => 'ascii','precision' => '32'),
'hook_filename' => array('type' => 'ascii','precision' => '255')
),
'pk' => array('hook_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_languages' => array(
'fd' => array(
'lang_id' => array('type' => 'ascii','precision' => '5','nullable' => False),
@ -138,16 +126,6 @@ $phpgw_baseline = array(
'ix' => array(),
'uc' => array(array('lang','app_name','message_id'))
),
'egw_nextid' => array(
'fd' => array(
'id' => array('type' => 'int','precision' => '4'),
'appname' => array('type' => 'ascii','precision' => '16','nullable' => False)
),
'pk' => array('appname'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_categories' => array(
'fd' => array(
'cat_id' => array('type' => 'auto','meta' => 'category','precision' => '4','nullable' => False),

View File

@ -1036,3 +1036,27 @@ function phpgwapi_upgrade14_3_905()
}
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '14.3.906';
}
/**
* Move content of (usually empty or for LDAP 2 rows) egw_nextid table to egw_config table and drop it
*/
function phpgwapi_upgrade14_3_906()
{
foreach($GLOBALS['egw_setup']->db->query('SELECT appname,id FROM egw_nextid', __LINE__, __FILE__) as $row)
{
common::next_id($row['appname'], $row['id']); // store it
}
$GLOBALS['egw_setup']->oProc->DropTable('egw_nextid');
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '14.3.907';
}
/**
* Move content of (usually empty or for LDAP 2 rows) egw_nextid table to egw_config table and drop it
*/
function phpgwapi_upgrade14_3_907()
{
$GLOBALS['egw_setup']->oProc->DropTable('egw_hooks');
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '14.3.908';
}

View File

@ -13,6 +13,8 @@
* @version $Id$
*/
use EGroupware\Api;
class setup
{
var $db;
@ -650,23 +652,12 @@ class setup
*/
function register_hooks($appname)
{
$setup_info = $GLOBALS['setup_info'];
if(!$appname)
{
return False;
}
if(!$this->hooks_table) // No hooks table yet
{
return False;
}
if (!is_object($this->hooks))
{
$this->hooks =& CreateObject('phpgwapi.hooks',$this->db,$this->hooks_table);
}
$this->hooks->register_hooks($appname,$setup_info[$appname]['hooks']);
Api\Hooks::read(true);
}
/**
@ -758,22 +749,12 @@ class setup
*/
function deregister_hooks($appname)
{
if(!$this->hooks_table) // No hooks table yet
{
return False;
}
if(!$appname)
{
return False;
}
//echo "DELETING hooks for: " . $setup_info[$appname]['name'];
if (!is_object($this->hooks))
{
$this->hooks =& CreateObject('phpgwapi.hooks',$this->db,$this->hooks_table);
}
return $this->hooks->register_hooks($appname);
Api\Hooks::read(true);
}
/**
@ -784,11 +765,7 @@ class setup
*/
function hook($location, $appname='')
{
if (!is_object($this->hooks))
{
$this->hooks =& CreateObject('phpgwapi.hooks',$this->db,$this->hooks_table);
}
return $this->hooks->single($location,$appname,True,True);
return Api\Hooks::single($location,$appname,True,True);
}
/**

View File

@ -607,7 +607,7 @@ class setup_cmd_ldap extends setup_cmd
// running all addAccount hooks (currently NOT working, as not all work in setup)
if ($this->add_account_hook === true)
{
$GLOBALS['egw']->hooks->process($account,array(),true);
Api\Hooks::process($account, array(), true);
}
elseif(is_callable($this->add_account_hook))
{