Passed
Push — 16.1 ( 647407...8fe467 )
by Ralf
12:04
created

Hooks::disable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * EGroupware API - Hooks
4
 *
5
 * @link http://www.egroupware.org
6
 * @author Dan Kuykendall <[email protected]>
7
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
8
 * Copyright (C) 2000, 2001 Dan Kuykendall
9
 * New method hooks and docu are written by <[email protected]>
10
 * @license http://opensource.org/licenses/lgpl-license.php LGPL - GNU Lesser General Public License
11
 * @package api
12
 * @version $Id$
13
 */
14
15
namespace EGroupware\Api;
16
17
/**
18
 * Allow applications to set and use hooks to communicate with each other
19
 *
20
 * Hooks need to be declared in the app's setup.inc.php file and
21
 * are cached in instance cache for 1h.
22
 *
23
 * Clearing instance cache or calling Api\Hooks::read(true) forces a new scan.
24
 *
25
 * Hooks can have one of the following formats:
26
 *  - static class method hooks are declared as:
27
 *	  $setup_info['appname']['hooks']['location'] = 'class::method';
28
 *	- method hooks, which are methods of a class. You can pass parameters to the call and
29
 *	  they can return values. They get declared in setup.inc.php as:
30
 *	  $setup_info['appname']['hooks']['location'] = 'app.class.method';
31
 *	- old type, which are included files. Values can only be passed by global values and they cant return anything.
32
 *	  Old declaration in setup.inc.php:
33
 *	  $setup_info['appname']['hooks'][] = 'location';
34
 */
35
class Hooks
36
{
37
	/**
38
	 * Hooks by location and appname
39
	 *
40
	 * @var array $location => $app => array($file, ...)
41
	 */
42
	protected static $locations;
43
44
	/**
45
	 * Executes all the hooks (the user has rights to) for a given location
46
	 *
47
	 * If no $order given, hooks are executed in the order of the applications!
48
	 *
49
	 * @param string|array $args location-name as string or array with keys location and
50
	 *	further data to be passed to the hook, if its a new method-hook
51
	 * @param string|array $order appname(s as value), which should be executes first
52
	 * @param boolean $no_permission_check if True execute all hooks, not only the ones a user has rights to
53
	 *	$no_permission_check should *ONLY* be used when it *HAS* to be. (jengo)
54
	 * @return array with results of each hook call (with appname as key) and value:
55
	 *	- False if no hook exists (should no longer be the case),
56
	 *	- True if old hook exists and
57
	 *  - array of return-values, if an app implements more then one hook
58
	 * 	- whatever the new method-hook returns (can be True or False too!)
59
	 */
60
	public static function process($args, $order = array(), $no_permission_check = False)
61
	{
62
		//echo "<p>".__METHOD__.'('.array2string($args).','.array2string($order).','.array2string($no_permission_check).")</p>\n";
63
		$location = is_array($args) ? (isset($args['hook_location']) ? $args['hook_location'] : $args['location']) : $args;
64
65
		if (!isset(self::$locations)) self::read();
66
		$hooks = self::$locations[$location];
67
		if (!isset($hooks) || empty($hooks)) return array();	// not a single app implements that hook
68
69
		$apps = array_keys($hooks);
70
		if (!$no_permission_check)
71
		{
72
			// on install of a new egroupware both hook-apps and user apps may be empty/not set
73
			$apps = array_intersect((array)$apps,array_keys((array)$GLOBALS['egw_info']['user']['apps']));
74
		}
75
		if ($order)
76
		{
77
			$apps = array_unique(array_merge((array)$order,$apps));
78
		}
79
		$results = array();
80
		foreach((array)$apps as $appname)
81
		{
82
			$results[$appname] = self::single($args,$appname,$no_permission_check);
83
		}
84
		return $results;
85
	}
86
87
	/**
88
	 * Executes a single hook of a given location and application
89
	 *
90
	 * @param string|array $args location-name as string or array with keys location, appname and
91
	 *	further data to be passed to the hook, if its a new method-hook
92
	 * @param string $appname name of the app, which's hook to execute, if empty the current app is used
93
	 * @param boolean $no_permission_check =false if True execute all hooks, not only the ones a user has rights to
94
	 *	$no_permission_check should *ONLY* be used when it *HAS* to be. (jengo)
95
	 * @param boolean $try_unregistered =false If true, try to include old file-hook anyway (for setup)
96
	 * @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!).
97
	 */
98
	public static function single($args, $appname = '', $no_permission_check = False, $try_unregistered = False)
99
	{
100
		//error_log(__METHOD__."(".array2string($args).",'$appname','$no_permission_check','$try_unregistered')");
101
102
		if (!isset(self::$locations)) self::read();
103
104
		if (!is_array($args)) $args = array('location' => $args);
105
		$location = isset($args['hook_location']) ? $args['hook_location'] : $args['location'];
106
107
		if (!$appname)
108
		{
109
			$appname = is_array($args) && isset($args['appname']) ? $args['appname'] : $GLOBALS['egw_info']['flags']['currentapp'];
110
		}
111
		// excute hook only if $no_permission_check or user has run-rights for app
112
		if (!($no_permission_check || isset($GLOBALS['egw_info']['user']['apps'][$appname])))
113
		{
114
			return false;
115
		}
116
117
		$ret = array();
118
		foreach((array)self::$locations[$location][$appname] as $hook)
119
		{
120
			try {
121
				// old style file hook
122
				if ($hook[0] == '/')
123
				{
124
					if (!file_exists(EGW_SERVER_ROOT.$hook))
125
					{
126
						error_log(__METHOD__."() old style hook file '$hook' not found --> ignored!");
127
						continue;
128
					}
129
					include(EGW_SERVER_ROOT.$hook);
130
					return true;
131
				}
132
133
				list($class, $method) = explode('::', $hook);
134
135
				// static method of an autoloadable class
136
				if (isset($method) && class_exists($class))
137
				{
138
					if (is_callable($hook)) $ret[] = call_user_func($hook, $args);
139
				}
140
				// app.class.method or not autoloadable class
141
				else
142
				{
143
					$ret[] = ExecMethod2($hook, $args);
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod2() has been deprecated with message: use autoloadable class-names, instanciate and call method or use static methods

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
144
				}
145
			}
146
			catch (Api\Exception\AssertionFailed $e)
0 ignored issues
show
Bug introduced by
The class EGroupware\Api\Api\Exception\AssertionFailed does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
147
			{
148
				if (preg_match('/ file .+ not found!$/', $e->getMessage()))
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
149
				{
150
					// ignore not found hook
151
				}
152
				else
153
				{
154
					throw $e;
155
				}
156
			}
157
		}
158
159
		// hooks only existing in filesystem used by setup
160
		if (!$ret && $try_unregistered && file_exists(EGW_SERVER_ROOT.($hook='/'.$appname.'/inc/hook_'.$location.'.inc.php')))
161
		{
162
			include(EGW_SERVER_ROOT.$hook);
163
			return true;
164
		}
165
166
		if (!$ret) return false;
167
168
		return count($ret) == 1 ? $ret[0] : $ret;
169
	}
170
171
	/**
172
	 * loop through the applications and count the apps implementing a hooks
173
	 *
174
	 * @param string $location location-name
175
	 * @return int the number of found hooks
176
	 */
177
	public static function count($location)
178
	{
179
		if (!isset(self::$locations)) self::read();
180
181
		return count(self::$locations[$location]);
182
	}
183
184
	/**
185
	 * check if a given hook for an  application is registered
186
	 *
187
	 * @param string $location location-name
188
	 * @param string $app appname
189
	 * @param boolean $return_methods =false true: return hook-method(s)
190
	 * @return int|array the number of found hooks or for $return_methods array with methods
191
	 */
192
	public static function exists($location, $app, $return_methods=false)
193
	{
194
		if (!isset(self::$locations)) self::read();
195
196
		//error_log(__METHOD__.__LINE__.array2string(self::$locations[$location]));
197
		return $return_methods ? self::$locations[$location][$app] : count(self::$locations[$location][$app]);
198
	}
199
200
	/**
201
	 * check which apps implement a given hook
202
	 *
203
	 * @param string $location location-name
204
	 * @return array of apps implementing given hook
205
	 */
206
	public static function implemented($location)
207
	{
208
		if (!isset(self::$locations)) self::read();
209
210
		//error_log(__METHOD__.__LINE__.array2string(self::$locations[$location]));
211
		return isset(self::$locations[$location]) ? array_keys(self::$locations[$location]) : array();
212
	}
213
214
	/**
215
	 * Disable a hook for this request
216
	 *
217
	 * @param string $hook
218
	 * @return boolean true if hook existed, false otherwise
219
	 */
220
	static public function disable($hook)
221
	{
222
		if (!isset(self::$locations)) self::read();
223
224
		$ret = isset(self::$locations[$hook]);
225
226
		unset(self::$locations[$hook]);
227
228
		return $ret;
229
	}
230
231
	/**
232
	 * Read all hooks into self::$locations
233
	 *
234
	 * @param boolan $force_rescan =false true: do not use instance cache
235
	 */
236
	public static function read($force_rescan=false)
237
	{
238
		//$starttime = microtime(true);
239
		if ($force_rescan) Cache::unsetInstance(__CLASS__, 'locations');
240
241
		self::$locations = Cache::getInstance(__CLASS__, 'locations', function()
242
		{
243
			// if we run in setup, we need to read installed apps first
244
			if (!$GLOBALS['egw_info']['apps'])
245
			{
246
				$applications = new Egw\Applications();
247
				$applications->read_installed_apps();
248
			}
249
250
			// read all apps using just filesystem data
251
			$locations = array();
252
			foreach(array_merge(array('api'), array_keys($GLOBALS['egw_info']['apps'])) as $appname)
253
			{
254
				if ($appname[0] == '.' || !is_dir(EGW_SERVER_ROOT.'/'.$appname)) continue;
255
256
				$f = EGW_SERVER_ROOT . '/' . $appname . '/setup/setup.inc.php';
257
				$setup_info = array($appname => array());
258
				if(@file_exists($f)) include($f);
259
260
				// some apps have setup_info for more then themselfs (eg. api for groupdav)
261
				foreach($setup_info as $appname => $data)
262
				{
263
					foreach((array)$data['hooks'] as $location => $methods)
264
					{
265
						if (is_int($location))
266
						{
267
							$location = $methods;
268
							$methods = '/'.$appname.'/inc/hook_'.$methods.'.inc.php';
269
						}
270
						$locations[$location][$appname] = (array)$methods;
271
					}
272
				}
273
			}
274
			return $locations;
275
		}, array(), 3600);
276
277
		//error_log(__METHOD__."() took ".number_format(1000*(microtime(true)-$starttime), 1)."ms, size=".Vfs::hsize(strlen(json_encode(self::$locations))));
278
	}
279
280
	/**
281
	 * Static function to build pgp encryption sidebox menu
282
	 * @param type $appname application name
283
	 */
284
	public static function pgp_encryption_menu($appname)
285
	{
286
		if (Header\UserAgent::mobile()) return;
287
288
		// PGP Encryption (Mailvelope plugin) restore/backup menu
289
		$file = Array(
290
			'Backup/Restore ...' => 'javascript:app.'.$appname.'.mailvelopeCreateBackupRestoreDialog();',
291
			'sendToBottom' => true
292
		);
293
		display_sidebox($appname, lang('PGP Encryption'), $file);
0 ignored issues
show
Deprecated Code introduced by
The function display_sidebox() has been deprecated with message: use $GLOBALS['egw']->framework->sidebox()

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
294
	}
295
}
296