1 | <?php |
||||||
2 | /** |
||||||
3 | * EGroupware admin - admin command base class |
||||||
4 | * |
||||||
5 | * @link http://www.egroupware.org |
||||||
6 | * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||||
7 | * @package admin |
||||||
8 | * @copyright (c) 2007-18 by Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||||
9 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||||
10 | */ |
||||||
11 | |||||||
12 | use EGroupware\Api; |
||||||
13 | use EGroupware\Api\Acl; |
||||||
14 | |||||||
15 | /** |
||||||
16 | * Admin comand base class |
||||||
17 | * |
||||||
18 | * Admin commands should be used to implement and log (!) all actions admins carry |
||||||
19 | * out using the administrative rights (regular users cant do). |
||||||
20 | * |
||||||
21 | * They are stored in DB table egw_admin_queue which builds a persitent log |
||||||
22 | * of administrative actions cared out on an EGroupware installation. |
||||||
23 | * Commands can be marked deleted (canceled for scheduled commands), |
||||||
24 | * but they are never deleted for the table to implement a persistent log! |
||||||
25 | * |
||||||
26 | * All administrative actions are encapsulated in classes derived from this |
||||||
27 | * abstract base class implementing an exec method to carry out the command. |
||||||
28 | * |
||||||
29 | * @property-read int $created Creation timestamp |
||||||
30 | * @property-read int $creator Creator user-id |
||||||
31 | * @property-read string $creator_email rfc822 address ("Name <[email protected]>") of creator |
||||||
32 | * @property-read int $modified Modification timestamp |
||||||
33 | * @property-read int|NULL $scheduled timestamp if command is not run immediatly, |
||||||
34 | * but scheduled to run automatic by the system at a later point in time |
||||||
35 | * @property-read int $modifier Modifier user-id |
||||||
36 | * @property-read string $modifier_email rfc822 address ("Name <[email protected]>") of modifier |
||||||
37 | * @property int|NULL $requested User who requested the change (not current user!) |
||||||
38 | * @property string|NULL $requested_email rfc822 address ("Name <[email protected]>") of requested |
||||||
39 | * @property string|NULL $comment comment, eg. reasoning why change was requested |
||||||
40 | * @property-read int|NULL $errno Numerical error-code or NULL on success |
||||||
41 | * @property-read string|NULL $error Error message or NULL on success |
||||||
42 | * @property array|string|NULL $result Result message indicating what happened, or NULL on failure |
||||||
43 | * @property-read int $id $id of command/row in egw_admin_queue table |
||||||
44 | * @property-read string $uid uuid of command (necessary if command is send to a remote system to execute) |
||||||
45 | * @property int|NULL $remote_id id of remote system, if command is not meant to run on local system |
||||||
46 | * foreign key into egw_admin_remote (table of remote systems administrated by this one) |
||||||
47 | * @property-read int $account account_id of user affected by this cmd or NULL |
||||||
48 | * @property-read string $app app-name affected by this cmd or NULL |
||||||
49 | * @property-read string $parent parent cmd (with rrule) of single periodic execution |
||||||
50 | * @property-read string $rrule rrule for periodic execution |
||||||
51 | * @property int $rrule_start optional start timestamp for rrule, default $created time |
||||||
52 | * @property string async_job_id optional name of async job for periodic-run, default "admin-cmd-$id" |
||||||
53 | * @property array set optional New values set by the command |
||||||
54 | * @property array old optional Previous values before the command was run |
||||||
55 | */ |
||||||
56 | abstract class admin_cmd |
||||||
57 | { |
||||||
58 | const deleted = 0; |
||||||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||||||
59 | const scheduled = 1; |
||||||
0 ignored issues
–
show
|
|||||||
60 | const successful = 2; |
||||||
0 ignored issues
–
show
|
|||||||
61 | const failed = 3; |
||||||
0 ignored issues
–
show
|
|||||||
62 | const pending = 4; |
||||||
0 ignored issues
–
show
|
|||||||
63 | const queued = 5; // command waits to be fetched from remote |
||||||
0 ignored issues
–
show
|
|||||||
64 | |||||||
65 | /** |
||||||
66 | * Status which stil need passwords available |
||||||
67 | * |
||||||
68 | * @var array |
||||||
69 | */ |
||||||
70 | static $require_pw_stati = array(self::scheduled,self::pending,self::queued); |
||||||
71 | |||||||
72 | /** |
||||||
73 | * The status of the command, one of either scheduled, successful, failed or deleted |
||||||
74 | * |
||||||
75 | * @var int |
||||||
76 | */ |
||||||
77 | protected $status = self::successful; |
||||||
78 | |||||||
79 | static $stati = array( |
||||||
80 | admin_cmd::scheduled => 'scheduled', |
||||||
81 | admin_cmd::successful => 'successful', |
||||||
82 | admin_cmd::failed => 'failed', |
||||||
83 | admin_cmd::deleted => 'deleted', |
||||||
84 | admin_cmd::pending => 'pending', |
||||||
85 | admin_cmd::queued => 'queued', |
||||||
86 | ); |
||||||
87 | |||||||
88 | protected $created; |
||||||
89 | protected $creator; |
||||||
90 | protected $creator_email; |
||||||
91 | private $scheduled; |
||||||
92 | private $modified; |
||||||
93 | private $modifier; |
||||||
94 | private $modifier_email; |
||||||
95 | protected $error; |
||||||
96 | protected $errno; |
||||||
97 | public $requested; |
||||||
98 | public $requested_email; |
||||||
99 | public $comment; |
||||||
100 | private $id; |
||||||
101 | protected $uid; |
||||||
102 | private $type = __CLASS__; |
||||||
103 | public $remote_id; |
||||||
104 | protected $account; |
||||||
105 | protected $app; |
||||||
106 | protected $rrule; |
||||||
107 | protected $parent; |
||||||
108 | |||||||
109 | /** |
||||||
110 | * Display name of command, default ucfirst(str_replace(['_cmd_', '_'], ' ', __CLASS__)) |
||||||
111 | */ |
||||||
112 | const NAME = null; |
||||||
113 | |||||||
114 | /** |
||||||
115 | * Stores the data of the derived classes |
||||||
116 | * |
||||||
117 | * @var array |
||||||
118 | */ |
||||||
119 | private $data = array(); |
||||||
120 | |||||||
121 | /** |
||||||
122 | * Instance of the Api\Accounts class, after calling instanciate_accounts! |
||||||
123 | * |
||||||
124 | * @var Api\Accounts |
||||||
125 | */ |
||||||
126 | static protected $accounts; |
||||||
127 | |||||||
128 | /** |
||||||
129 | * Instance of the Acl class, after calling instanciate_acl! |
||||||
130 | * |
||||||
131 | * @var Acl |
||||||
132 | */ |
||||||
133 | static protected $acl; |
||||||
134 | |||||||
135 | /** |
||||||
136 | * Instance of Api\Storage\Base for egw_admin_queue |
||||||
137 | * |
||||||
138 | * @var Api\Storage\Base |
||||||
139 | */ |
||||||
140 | static private $sql; |
||||||
141 | |||||||
142 | /** |
||||||
143 | * Instance of Api\Storage\Base for egw_admin_remote |
||||||
144 | * |
||||||
145 | * @var Api\Storage\Base |
||||||
146 | */ |
||||||
147 | static private $remote; |
||||||
148 | |||||||
149 | /** |
||||||
150 | * Executes the command |
||||||
151 | * |
||||||
152 | * @param boolean $check_only =false only run the checks (and throw the exceptions), but not the command itself |
||||||
153 | * @return string success message |
||||||
154 | * @throws Exception() |
||||||
155 | */ |
||||||
156 | protected abstract function exec($check_only=false); |
||||||
157 | |||||||
158 | /** |
||||||
159 | * Return a title / string representation for a given command, eg. to display it |
||||||
160 | * |
||||||
161 | * @return string |
||||||
162 | */ |
||||||
163 | function __tostring() |
||||||
164 | { |
||||||
165 | return $this->type; |
||||||
166 | } |
||||||
167 | |||||||
168 | /** |
||||||
169 | * Generate human readable name of object |
||||||
170 | * |
||||||
171 | * @return string |
||||||
172 | */ |
||||||
173 | public static function name() |
||||||
174 | { |
||||||
175 | if (self::NAME) return self::NAME; |
||||||
176 | |||||||
177 | return ucfirst(str_replace(['_cmd_', '_', '\\'], ' ', get_called_class())); |
||||||
178 | } |
||||||
179 | |||||||
180 | /** |
||||||
181 | * Constructor |
||||||
182 | * |
||||||
183 | * @param array $data class vars as array |
||||||
184 | */ |
||||||
185 | function __construct(array $data) |
||||||
186 | { |
||||||
187 | $this->created = time(); |
||||||
0 ignored issues
–
show
|
|||||||
188 | $this->creator = $GLOBALS['egw_info']['user']['account_id']; |
||||||
0 ignored issues
–
show
|
|||||||
189 | $this->creator_email = admin_cmd::user_email(); |
||||||
0 ignored issues
–
show
|
|||||||
190 | |||||||
191 | $this->type = get_class($this); |
||||||
192 | |||||||
193 | foreach($data as $name => $value) |
||||||
194 | { |
||||||
195 | $this->$name = $name == 'data' && !is_array($value) ? json_php_unserialize($value) : $value; |
||||||
196 | } |
||||||
197 | //_debug_array($this); exit; |
||||||
198 | } |
||||||
199 | |||||||
200 | /** |
||||||
201 | * runs the command either immediatly ($time=null) or shedules it for the given time |
||||||
202 | * |
||||||
203 | * The command will be written to the database queue, incl. its scheduled start time or execution status |
||||||
204 | * |
||||||
205 | * @param int $time =null timestamp to run the command or null to run it immediatly |
||||||
206 | * @param boolean $set_modifier =null should the current user be set as modifier, default true |
||||||
207 | * @param booelan $skip_checks =false do not yet run the checks for a scheduled command |
||||||
0 ignored issues
–
show
The type
booelan was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
208 | * @param boolean $dry_run =false only run checks, NOT command itself |
||||||
209 | * @return mixed return value of the command |
||||||
210 | * @throws Exceptions on error |
||||||
211 | */ |
||||||
212 | function run($time=null,$set_modifier=true,$skip_checks=false,$dry_run=false) |
||||||
213 | { |
||||||
214 | if (!is_null($time)) |
||||||
215 | { |
||||||
216 | $this->scheduled = $time; |
||||||
0 ignored issues
–
show
|
|||||||
217 | $this->status = admin_cmd::scheduled; |
||||||
218 | $ret = lang('Command scheduled to run at %1',date('Y-m-d H:i',$time)); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with date('Y-m-d H:i', $time) .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
219 | // running the checks of the arguments for local commands, if not explicitly requested to not run them |
||||||
220 | if (!$this->remote_id && !$skip_checks) |
||||||
0 ignored issues
–
show
The expression
$this->remote_id of type integer|null is loosely compared to false ; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||||
221 | { |
||||||
222 | try { |
||||||
223 | $this->exec(true); |
||||||
224 | } |
||||||
225 | catch (Exception $e) { |
||||||
226 | _egw_log_exception($e); |
||||||
227 | $this->error = $e->getMessage(); |
||||||
0 ignored issues
–
show
|
|||||||
228 | $ret = $this->errno = $e->getCode(); |
||||||
0 ignored issues
–
show
|
|||||||
229 | $this->status = admin_cmd::failed; |
||||||
230 | $dont_save = true; |
||||||
231 | } |
||||||
232 | } |
||||||
233 | } |
||||||
234 | else |
||||||
235 | { |
||||||
236 | try { |
||||||
237 | if (!$this->remote_id) |
||||||
0 ignored issues
–
show
The expression
$this->remote_id of type integer|null is loosely compared to false ; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||||
238 | { |
||||||
239 | $ret = $this->exec($dry_run); |
||||||
240 | } |
||||||
241 | else |
||||||
242 | { |
||||||
243 | $ret = $this->remote_exec($dry_run); |
||||||
0 ignored issues
–
show
The call to
admin_cmd::remote_exec() has too many arguments starting with $dry_run .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
244 | } |
||||||
245 | if (is_null($this->status)) $this->status = admin_cmd::successful; |
||||||
0 ignored issues
–
show
|
|||||||
246 | } |
||||||
247 | catch (Exception $e) { |
||||||
248 | _egw_log_exception($e); |
||||||
249 | $this->error = $e->getMessage(); |
||||||
250 | $ret = $this->errno = $e->getCode(); |
||||||
251 | $this->status = admin_cmd::failed; |
||||||
252 | } |
||||||
253 | } |
||||||
254 | $this->result = $ret; |
||||||
255 | if (!$dont_save && !$dry_run && !$this->save($set_modifier)) |
||||||
256 | { |
||||||
257 | throw new Api\Db\Exception(lang('Error saving the command!')); |
||||||
258 | } |
||||||
259 | if ($e instanceof Exception) |
||||||
260 | { |
||||||
261 | throw $e; |
||||||
262 | } |
||||||
263 | return $ret; |
||||||
264 | } |
||||||
265 | |||||||
266 | /** |
||||||
267 | * Runs a command on a remote install |
||||||
268 | * |
||||||
269 | * This is a very basic remote procedure call to an other egw instance. |
||||||
270 | * The payload / command data is send as POST request to the remote installs admin/remote.php script. |
||||||
271 | * The remote domain (eGW instance) and the secret authenticating the request are send as GET parameters. |
||||||
272 | * |
||||||
273 | * To authenticate with the installation we use a secret, which is a md5 hash build from the uid |
||||||
274 | * of the command (to not allow to send new commands with an earsdroped secret) and the md5 hash |
||||||
275 | * of the md5 hash of the config password and the install_id (egw_admin_remote.remote_hash) |
||||||
276 | * |
||||||
277 | * @return string sussess message |
||||||
278 | * @throws Exception(lang('Invalid remote id or name "%1"!',$this->remote_id),997) or other Exceptions reported from remote |
||||||
279 | */ |
||||||
280 | protected function remote_exec() |
||||||
281 | { |
||||||
282 | if (!($remote = $this->read_remote($this->remote_id))) |
||||||
283 | { |
||||||
284 | throw new Api\Exception\WrongUserinput(lang('Invalid remote id or name "%1"!',$this->remote_id),997); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $this->remote_id .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
285 | } |
||||||
286 | if (!$this->uid) |
||||||
287 | { |
||||||
288 | $this->save(); // to get the uid |
||||||
289 | } |
||||||
290 | $secret = md5($this->uid.$remote['remote_hash']); |
||||||
291 | |||||||
292 | $postdata = $this->as_array(); |
||||||
293 | if (is_object($GLOBALS['egw']->translation)) |
||||||
294 | { |
||||||
295 | $postdata = Api\Translation::convert($postdata,Api\Translation::charset(),'utf-8'); |
||||||
296 | } |
||||||
297 | // dont send the id's which have no meaning on the remote install |
||||||
298 | foreach(array('id','creator','modifier','requested','remote_id') as $name) |
||||||
299 | { |
||||||
300 | unset($postdata[$name]); |
||||||
301 | } |
||||||
302 | $opts = array('http' => |
||||||
303 | array( |
||||||
304 | 'method' => 'POST', |
||||||
305 | 'header' => 'Content-type: application/x-www-form-urlencoded', |
||||||
306 | 'content' => http_build_query($postdata), |
||||||
307 | ) |
||||||
308 | ); |
||||||
309 | $url = $remote['remote_url'].'/admin/remote.php?domain='.urlencode($remote['remote_domain']).'&secret='.urlencode($secret); |
||||||
310 | //echo "sending command to $url\n"; _debug_array($opts); |
||||||
311 | $http_response_header = null; |
||||||
0 ignored issues
–
show
|
|||||||
312 | if (!($message = @file_get_contents($url, false, stream_context_create($opts)))) |
||||||
313 | { |
||||||
314 | throw new Api\Exception(lang('Could not remote execute the command').': '.$http_response_header[0]); |
||||||
315 | } |
||||||
316 | //echo "got: $message\n"; |
||||||
317 | |||||||
318 | if (($value = json_php_unserialize($message)) !== false && $message !== serialize(false)) |
||||||
0 ignored issues
–
show
|
|||||||
319 | { |
||||||
320 | $message = $value; |
||||||
321 | } |
||||||
322 | if (is_object($GLOBALS['egw']->translation)) |
||||||
323 | { |
||||||
324 | $message = Api\Translation::convert($message,'utf-8'); |
||||||
325 | } |
||||||
326 | $matches = null; |
||||||
327 | if (is_string($message) && preg_match('/^([0-9]+) (.*)$/',$message,$matches)) |
||||||
328 | { |
||||||
329 | throw new Api\Exception($matches[2],(int)$matches[1]); |
||||||
330 | } |
||||||
331 | return $message; |
||||||
332 | } |
||||||
333 | |||||||
334 | /** |
||||||
335 | * Delete / canncels a scheduled command |
||||||
336 | * |
||||||
337 | * @return boolean true on success, false otherwise |
||||||
338 | */ |
||||||
339 | function delete() |
||||||
340 | { |
||||||
341 | $this->cancel_periodic_job(); |
||||||
342 | if ($this->status != admin_cmd::scheduled) return false; |
||||||
343 | |||||||
344 | $this->status = admin_cmd::deleted; |
||||||
345 | |||||||
346 | return $this->save(); |
||||||
347 | } |
||||||
348 | |||||||
349 | /** |
||||||
350 | * Saving the object to the database |
||||||
351 | * |
||||||
352 | * @param boolean $set_modifier =true set the current user as modifier or 0 (= run by the system) |
||||||
353 | * @return boolean true on success, false otherwise |
||||||
354 | */ |
||||||
355 | function save($set_modifier=true) |
||||||
356 | { |
||||||
357 | admin_cmd::_instanciate_sql(); |
||||||
358 | |||||||
359 | // check if uid already exists --> set the id to not try to insert it again (resulting in SQL error) |
||||||
360 | if (!$this->id && $this->uid && (list($other) = self::$sql->search(array('cmd_uid' => $this->uid)))) |
||||||
361 | { |
||||||
362 | $this->id = $other['id']; |
||||||
0 ignored issues
–
show
|
|||||||
363 | } |
||||||
364 | if (!is_null($this->id)) |
||||||
0 ignored issues
–
show
|
|||||||
365 | { |
||||||
366 | $this->modified = time(); |
||||||
0 ignored issues
–
show
|
|||||||
367 | $this->modifier = $set_modifier ? $GLOBALS['egw_info']['user']['account_id'] : 0; |
||||||
0 ignored issues
–
show
|
|||||||
368 | if ($set_modifier) $this->modifier_email = admin_cmd::user_email(); |
||||||
0 ignored issues
–
show
|
|||||||
369 | } |
||||||
370 | $vars = get_object_vars($this); // does not work in php5.1.2 due a bug |
||||||
371 | |||||||
372 | // data is stored serialized |
||||||
373 | // paswords are masked / removed, if we dont need them anymore |
||||||
374 | $vars['data'] = in_array($this->status, self::$require_pw_stati) ? |
||||||
375 | json_encode($this->data) : self::mask_passwords($this->data); |
||||||
376 | |||||||
377 | // skip EGroupware\\ prefix in new class-names, as value gets too long for column otherwise |
||||||
378 | if (strpos($this->type, 'EGroupware\\') === 0) |
||||||
379 | { |
||||||
380 | $vars['type'] = substr($this->type, 11); |
||||||
381 | } |
||||||
382 | |||||||
383 | admin_cmd::$sql->init($vars); |
||||||
384 | if (admin_cmd::$sql->save() != 0) |
||||||
385 | { |
||||||
386 | return false; |
||||||
387 | } |
||||||
388 | if (!$this->id) |
||||||
389 | { |
||||||
390 | $this->id = admin_cmd::$sql->data['id']; |
||||||
391 | // if the cmd has no uid yet, we create one from our id and the install-id of this eGW instance |
||||||
392 | if (!$this->uid) |
||||||
393 | { |
||||||
394 | $this->uid = $this->id.'-'.$GLOBALS['egw_info']['server']['install_id']; |
||||||
0 ignored issues
–
show
|
|||||||
395 | admin_cmd::$sql->save(array('uid' => $this->uid)); |
||||||
396 | } |
||||||
397 | } |
||||||
398 | // install an async job, if we saved a scheduled job |
||||||
399 | if ($this->status == admin_cmd::scheduled && empty($this->rrule)) |
||||||
400 | { |
||||||
401 | admin_cmd::_set_async_job(); |
||||||
402 | } |
||||||
403 | // schedule periodic execution, if we have an rrule |
||||||
404 | elseif (!empty($this->rrule) && $this->status != admin_cmd::deleted) |
||||||
405 | { |
||||||
406 | $this->set_periodic_job(); |
||||||
407 | } |
||||||
408 | // existing object with no rrule, cancle evtl. running periodic job |
||||||
409 | elseif($vars['id']) |
||||||
410 | { |
||||||
411 | $this->cancel_periodic_job(); |
||||||
412 | } |
||||||
413 | return true; |
||||||
414 | } |
||||||
415 | |||||||
416 | /** |
||||||
417 | * Mask / remove passwords in $data |
||||||
418 | * |
||||||
419 | * @param string|array $data json or php-encoded string or array |
||||||
420 | * @param boolean $return_serialized =true true: return json serialized string, false: return array |
||||||
421 | * @return string|array see $return_serialized |
||||||
422 | */ |
||||||
423 | static function mask_passwords($data, $return_serialized=true) |
||||||
424 | { |
||||||
425 | if (!is_array($data)) |
||||||
426 | { |
||||||
427 | $data = json_php_unserialize($data); |
||||||
428 | } |
||||||
429 | foreach($data as $key => &$value) |
||||||
430 | { |
||||||
431 | if (is_array($value)) |
||||||
432 | { |
||||||
433 | $value = self::mask_passwords($value, false); |
||||||
434 | } |
||||||
435 | elseif (preg_match('/(pw|passwd_?\d*|(?<!change)password|db_pass|secret)$/i', $key)) |
||||||
436 | { |
||||||
437 | $value = str_repeat('*', strlen($value)); |
||||||
438 | } |
||||||
439 | } |
||||||
440 | return $return_serialized ? json_encode($data) : $data; |
||||||
441 | } |
||||||
442 | |||||||
443 | /** |
||||||
444 | * Reading a command from the queue returning the comand object |
||||||
445 | * |
||||||
446 | * @static |
||||||
447 | * @param int|string $id id or uid of the command |
||||||
448 | * @return admin_cmd or null if record not found |
||||||
449 | * @throws Api\Exception\WrongParameter if class does not exist or is no instance of admin_cmd |
||||||
450 | */ |
||||||
451 | static function read($id) |
||||||
452 | { |
||||||
453 | admin_cmd::_instanciate_sql(); |
||||||
454 | |||||||
455 | $keys = is_numeric($id) ? array('id' => $id) : array('uid' => $id); |
||||||
456 | if (!($data = admin_cmd::$sql->read($keys))) |
||||||
457 | { |
||||||
458 | return $data; |
||||||
459 | } |
||||||
460 | return admin_cmd::instanciate($data); |
||||||
461 | } |
||||||
462 | |||||||
463 | /** |
||||||
464 | * Instanciated the object / subclass using the given data |
||||||
465 | * |
||||||
466 | * @static |
||||||
467 | * @param array $data |
||||||
468 | * @return admin_cmd |
||||||
469 | * @throws Api\Exception\WrongParameter if class does not exist or is no instance of admin_cmd |
||||||
470 | */ |
||||||
471 | static function instanciate(array $data) |
||||||
472 | { |
||||||
473 | if (isset($data['data']) && !is_array($data['data'])) |
||||||
474 | { |
||||||
475 | $data['data'] = json_php_unserialize($data['data']); |
||||||
476 | } |
||||||
477 | if (!(class_exists($class = 'EGroupware\\'.$data['type']) || // namespaced class |
||||||
478 | class_exists($class = $data['type'])) || $data['type'] == 'admin_cmd') |
||||||
479 | { |
||||||
480 | throw new Api\Exception\WrongParameter(lang('Unknown command %1!',$class), 10); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $class .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
481 | } |
||||||
482 | $cmd = new $class($data); |
||||||
483 | |||||||
484 | if ($cmd instanceof admin_cmd) // dont allow others classes to be executed that way! |
||||||
485 | { |
||||||
486 | return $cmd; |
||||||
487 | } |
||||||
488 | throw new Api\Exception\WrongParameter(lang('%1 is no command!',$class), 10); |
||||||
489 | } |
||||||
490 | |||||||
491 | /** |
||||||
492 | * calling get_rows of our static Api\Storage\Base instance |
||||||
493 | * |
||||||
494 | * @static |
||||||
495 | * @param array $query |
||||||
496 | * @param array &$rows |
||||||
497 | * @param array $readonlys |
||||||
498 | * @return int |
||||||
499 | */ |
||||||
500 | static function get_rows($query,&$rows,$readonlys) |
||||||
501 | { |
||||||
502 | admin_cmd::_instanciate_sql(); |
||||||
503 | |||||||
504 | if ((string)$query['col_filter']['remote_id'] === '0') |
||||||
505 | { |
||||||
506 | $query['col_filter']['remote_id'] = null; |
||||||
507 | } |
||||||
508 | if ((string)$query['col_filter']['periodic'] === '0') |
||||||
509 | { |
||||||
510 | $query['col_filter']['rrule'] = null; |
||||||
511 | } |
||||||
512 | else if ((string)$query['col_filter']['periodic'] === '1') |
||||||
513 | { |
||||||
514 | $query['col_filter'][] = 'cmd_rrule IS NOT NULL'; |
||||||
515 | } |
||||||
516 | unset($query['col_filter']['periodic']); |
||||||
517 | if($query['col_filter']['parent']) |
||||||
518 | { |
||||||
519 | $query['col_filter']['parent'] = (int)$query['col_filter']['parent']; |
||||||
520 | } |
||||||
521 | |||||||
522 | $total = admin_cmd::$sql->get_rows($query,$rows,$readonlys); |
||||||
523 | |||||||
524 | if (!$rows) return 0; |
||||||
525 | |||||||
526 | $async = new Api\Asyncservice(); |
||||||
527 | foreach($rows as &$row) |
||||||
528 | { |
||||||
529 | try { |
||||||
530 | $cmd = admin_cmd::instanciate($row); |
||||||
531 | $row['title'] = $cmd->__tostring(); // we call __tostring explicit, as a cast to string requires php5.2+ |
||||||
532 | } |
||||||
533 | catch (Exception $e) { |
||||||
534 | $row['title'] = $e->getMessage(); |
||||||
535 | } |
||||||
536 | |||||||
537 | $row['value'] = $cmd->value; |
||||||
0 ignored issues
–
show
The property
value does not exist on admin_cmd . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||||
538 | |||||||
539 | if(method_exists($cmd, 'summary')) |
||||||
540 | { |
||||||
541 | $row['data'] = $cmd->summary(); |
||||||
542 | } |
||||||
543 | else |
||||||
544 | { |
||||||
545 | $row['data'] = !($data = json_php_unserialize($row['data'])) ? '' : |
||||||
546 | json_encode($data+(empty($row['rrule'])?array():array('rrule' => $row['rrule'])), |
||||||
547 | JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); |
||||||
548 | } |
||||||
549 | if($row['rrule']) |
||||||
550 | { |
||||||
551 | $rrule = calendar_rrule::event2rrule(calendar_rrule::parseRrule($row['rrule'],true)+array( |
||||||
552 | 'start' => time(), |
||||||
553 | 'tzid'=> Api\DateTime::$server_timezone->getName() |
||||||
554 | )); |
||||||
555 | $row['rrule'] = ''.$rrule; |
||||||
556 | } |
||||||
557 | if(!$row['scheduled'] && $cmd && $cmd->async_job_id) |
||||||
558 | { |
||||||
559 | $job = $async->read($cmd->async_job_id); |
||||||
560 | |||||||
561 | $row['scheduled'] = $job ? $job[$cmd->async_job_id]['next'] : null; |
||||||
562 | } |
||||||
563 | if ($row['status'] == admin_cmd::scheduled) |
||||||
564 | { |
||||||
565 | $row['class'] = 'AllowDelete'; |
||||||
566 | } |
||||||
567 | } |
||||||
568 | return $total; |
||||||
569 | } |
||||||
570 | |||||||
571 | /** |
||||||
572 | * Get list of stored or available (admin) cmd classes/types |
||||||
573 | * |
||||||
574 | * @return array class => label pairs |
||||||
575 | */ |
||||||
576 | static function get_cmd_labels() |
||||||
577 | { |
||||||
578 | return Api\Cache::getInstance(__CLASS__, 'cmd_labels', function() |
||||||
579 | { |
||||||
580 | admin_cmd::_instanciate_sql(); |
||||||
581 | |||||||
582 | // Need a new one to avoid column name modification |
||||||
583 | $sql = new Api\Storage\Base('admin','egw_admin_queue',null); |
||||||
584 | $labels = $sql->query_list('cmd_type'); |
||||||
585 | |||||||
586 | // for admin app we also add all available cmd objects |
||||||
587 | foreach(scandir(__DIR__) as $file) |
||||||
588 | { |
||||||
589 | $matches = null; |
||||||
590 | if (preg_match('/^class\.(admin_cmd_.*)\.inc\.php$/', $file, $matches)) |
||||||
591 | { |
||||||
592 | if (!isset($labels[$matches[1]])) |
||||||
593 | { |
||||||
594 | $labels[$matches[1]] = $matches[1]; |
||||||
595 | } |
||||||
596 | } |
||||||
597 | } |
||||||
598 | foreach($labels as $class => &$label) |
||||||
599 | { |
||||||
600 | if(class_exists($class)) |
||||||
601 | { |
||||||
602 | $label = $class::name(); |
||||||
603 | } |
||||||
604 | elseif (class_exists('EGroupware\\' . $class)) |
||||||
605 | { |
||||||
606 | $class = 'EGroupware\\' . $class; |
||||||
607 | $label = $class::name(); |
||||||
608 | } |
||||||
609 | } |
||||||
610 | |||||||
611 | // sort them alphabetic |
||||||
612 | uasort($labels, function($a, $b) |
||||||
613 | { |
||||||
614 | return strcasecmp($a, $b); |
||||||
615 | }); |
||||||
616 | |||||||
617 | return $labels; |
||||||
618 | }); |
||||||
619 | } |
||||||
620 | |||||||
621 | /** |
||||||
622 | * calling search method of our static Api\Storage\Base instance |
||||||
623 | * |
||||||
624 | * @static |
||||||
625 | * @param array|string $criteria array of key and data cols, OR a SQL query (content for WHERE), fully quoted (!) |
||||||
626 | * @param boolean|string|array $only_keys =true True returns only keys, False returns all cols. or |
||||||
627 | * comma seperated list or array of columns to return |
||||||
628 | * @param string $order_by ='' fieldnames + {ASC|DESC} separated by colons ',', can also contain a GROUP BY (if it contains ORDER BY) |
||||||
629 | * @param string|array $extra_cols ='' string or array of strings to be added to the SELECT, eg. "count(*) as num" |
||||||
630 | * @param string $wildcard ='' appended befor and after each criteria |
||||||
631 | * @param boolean $empty =false False=empty criteria are ignored in query, True=empty have to be empty in row |
||||||
632 | * @param string $op ='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together |
||||||
633 | * @param mixed $start =false if != false, return only maxmatch rows begining with start, or array($start,$num), or 'UNION' for a part of a union query |
||||||
634 | * @param array $filter =null if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards |
||||||
635 | * @return array |
||||||
636 | */ |
||||||
637 | static function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null) |
||||||
638 | { |
||||||
639 | admin_cmd::_instanciate_sql(); |
||||||
640 | |||||||
641 | return admin_cmd::$sql->search($criteria,$only_keys,$order_by,$extra_cols,$wildcard,$empty,$op,$start,$filter); |
||||||
642 | } |
||||||
643 | |||||||
644 | /** |
||||||
645 | * Instanciate our static Api\Storage\Base object for egw_admin_queue |
||||||
646 | * |
||||||
647 | * @static |
||||||
648 | */ |
||||||
649 | private static function _instanciate_sql() |
||||||
650 | { |
||||||
651 | if (is_null(admin_cmd::$sql)) |
||||||
652 | { |
||||||
653 | admin_cmd::$sql = new Api\Storage\Base('admin','egw_admin_queue',null,'cmd_'); |
||||||
654 | } |
||||||
655 | } |
||||||
656 | |||||||
657 | /** |
||||||
658 | * Instanciate our static Api\Storage\Base object for egw_admin_remote |
||||||
659 | * |
||||||
660 | * @static |
||||||
661 | */ |
||||||
662 | private static function _instanciate_remote() |
||||||
663 | { |
||||||
664 | if (is_null(admin_cmd::$remote)) |
||||||
665 | { |
||||||
666 | admin_cmd::$remote = new Api\Storage\Base('admin','egw_admin_remote'); |
||||||
667 | } |
||||||
668 | } |
||||||
669 | |||||||
670 | /** |
||||||
671 | * magic method to read a property, all non admin-cmd properties are stored in the data array |
||||||
672 | * |
||||||
673 | * @param string $property |
||||||
674 | * @return mixed |
||||||
675 | */ |
||||||
676 | function __get($property) |
||||||
677 | { |
||||||
678 | if (property_exists('admin_cmd',$property)) |
||||||
679 | { |
||||||
680 | return $this->$property; // making all (non static) class vars readonly available |
||||||
681 | } |
||||||
682 | switch($property) |
||||||
683 | { |
||||||
684 | case 'accounts': |
||||||
685 | self::_instanciate_accounts(); |
||||||
686 | return self::$accounts; |
||||||
687 | case 'data': |
||||||
688 | return $this->data; |
||||||
689 | } |
||||||
690 | return $this->data[$property]; |
||||||
691 | } |
||||||
692 | |||||||
693 | /** |
||||||
694 | * magic method to check if a property is set, all non admin-cmd properties are stored in the data array |
||||||
695 | * |
||||||
696 | * @param string $property |
||||||
697 | * @return boolean |
||||||
698 | */ |
||||||
699 | function __isset($property) |
||||||
700 | { |
||||||
701 | if (property_exists('admin_cmd',$property)) |
||||||
702 | { |
||||||
703 | return isset($this->$property); // making all (non static) class vars readonly available |
||||||
704 | } |
||||||
705 | return isset($this->data[$property]); |
||||||
706 | } |
||||||
707 | |||||||
708 | /** |
||||||
709 | * magic method to set a property, all non admin-cmd properties are stored in the data array |
||||||
710 | * |
||||||
711 | * @param string $property |
||||||
712 | * @param mixed $value |
||||||
713 | * @return mixed |
||||||
714 | */ |
||||||
715 | function __set($property,$value) |
||||||
716 | { |
||||||
717 | $this->data[$property] = $value; |
||||||
718 | } |
||||||
719 | |||||||
720 | /** |
||||||
721 | * magic method to unset a property, all non admin-cmd properties are stored in the data array |
||||||
722 | * |
||||||
723 | * @param string $property |
||||||
724 | */ |
||||||
725 | function __unset($property) |
||||||
726 | { |
||||||
727 | unset($this->data[$property]); |
||||||
728 | } |
||||||
729 | |||||||
730 | /** |
||||||
731 | * Return the whole object-data as array, it's a cast of the object to an array |
||||||
732 | * |
||||||
733 | * @return array |
||||||
734 | */ |
||||||
735 | function as_array() |
||||||
736 | { |
||||||
737 | if (version_compare(PHP_VERSION,'5.1.2','>')) |
||||||
738 | { |
||||||
739 | $vars = get_object_vars($this); // does not work in php5.1.2 due a bug |
||||||
740 | } |
||||||
741 | else |
||||||
742 | { |
||||||
743 | foreach(array_keys(get_class_vars(__CLASS__)) as $name) |
||||||
744 | { |
||||||
745 | $vars[$name] = $this->$name; |
||||||
746 | } |
||||||
747 | } |
||||||
748 | unset($vars['data']); |
||||||
749 | if ($this->data) $vars = array_merge($this->data,$vars); |
||||||
0 ignored issues
–
show
The expression
$this->data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||||||
750 | |||||||
751 | return $vars; |
||||||
752 | } |
||||||
753 | |||||||
754 | /** |
||||||
755 | * Check if the creator is still admin and has the neccessary admin rights |
||||||
756 | * |
||||||
757 | * @param string $extra_acl =null further admin rights to check, eg. 'account_access' |
||||||
758 | * @param int $extra_deny =null further admin rights to check, eg. 16 = deny edit Api\Accounts |
||||||
759 | * @throws Api\Exception\NoPermission\Admin |
||||||
760 | */ |
||||||
761 | protected function _check_admin($extra_acl=null,$extra_deny=null) |
||||||
762 | { |
||||||
763 | if ($this->creator) |
||||||
764 | { |
||||||
765 | admin_cmd::_instanciate_acl($this->creator); |
||||||
0 ignored issues
–
show
The method
admin_cmd::_instanciate_acl() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
766 | // todo: check only if and with $this->creator |
||||||
767 | if (!admin_cmd::$acl->check('run',1,'admin') && // creator is no longer admin |
||||||
768 | $extra_acl && $extra_deny && admin_cmd::$acl->check($extra_acl,$extra_deny,'admin')) // creator is explicitly forbidden to do something |
||||||
0 ignored issues
–
show
The expression
$extra_deny of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||||
769 | { |
||||||
770 | throw new Api\Exception\NoPermission\Admin(); |
||||||
771 | } |
||||||
772 | } |
||||||
773 | } |
||||||
774 | |||||||
775 | /** |
||||||
776 | * parse application names, titles or localised names and return array of app-names |
||||||
777 | * |
||||||
778 | * @param array $apps names, titles or localised names |
||||||
779 | * @return array of app-names |
||||||
780 | * @throws Api\Exception\WrongUserinput lang("Application '%1' not found (maybe not installed or misspelled)!",$name),8 |
||||||
781 | */ |
||||||
782 | static function parse_apps(array $apps) |
||||||
783 | { |
||||||
784 | foreach($apps as $key => $name) |
||||||
785 | { |
||||||
786 | if (!isset($GLOBALS['egw_info']['apps'][$name])) |
||||||
787 | { |
||||||
788 | foreach($GLOBALS['egw_info']['apps'] as $app => $data) // check against title and localised name |
||||||
789 | { |
||||||
790 | if (!strcasecmp($name,$data['title']) || !strcasecmp($name,lang($app))) |
||||||
791 | { |
||||||
792 | $apps[$key] = $name = $app; |
||||||
793 | break; |
||||||
794 | } |
||||||
795 | } |
||||||
796 | } |
||||||
797 | if (!isset($GLOBALS['egw_info']['apps'][$name])) |
||||||
798 | { |
||||||
799 | throw new Api\Exception\WrongUserinput(lang("Application '%1' not found (maybe not installed or misspelled)!",$name),8); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $name .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
800 | } |
||||||
801 | } |
||||||
802 | return $apps; |
||||||
803 | } |
||||||
804 | |||||||
805 | /** |
||||||
806 | * parse account name or id |
||||||
807 | * |
||||||
808 | * @param string|int $account account_id or account_lid |
||||||
809 | * @param boolean $allow_only_user =null true=only user, false=only groups, default both |
||||||
810 | * @return int/array account_id |
||||||
0 ignored issues
–
show
|
|||||||
811 | * @throws Api\Exception\WrongUserinput(lang("Unknown account: %1 !!!",$account), 15); |
||||||
812 | * @throws Api\Exception\WrongUserinput(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only_user?lang('user'):lang('group')), 16); |
||||||
813 | */ |
||||||
814 | static function parse_account($account,$allow_only_user=null) |
||||||
815 | { |
||||||
816 | admin_cmd::_instanciate_accounts(); |
||||||
817 | |||||||
818 | if (!($type = admin_cmd::$accounts->exists($account)) || |
||||||
819 | !is_numeric($id=$account) && !($id = admin_cmd::$accounts->name2id($account))) |
||||||
820 | { |
||||||
821 | throw new Api\Exception\WrongUserinput(lang("Unknown account: %1 !!!",$account), 15); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $account .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
822 | } |
||||||
823 | if (!is_null($allow_only_user) && $allow_only_user !== ($type == 1)) |
||||||
824 | { |
||||||
825 | throw new Api\Exception\WrongUserinput(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only_user?lang('user'):lang('group')), 16); |
||||||
826 | } |
||||||
827 | if ($type == 2 && $id > 0) $id = -$id; // groups use negative id's internally, fix it, if user given the wrong sign |
||||||
828 | |||||||
829 | return $id; |
||||||
830 | } |
||||||
831 | |||||||
832 | /** |
||||||
833 | * parse account names or ids |
||||||
834 | * |
||||||
835 | * @param string|int|array $accounts array or comma-separated account_id's or account_lid's |
||||||
836 | * @param boolean $allow_only_user =null true=only user, false=only groups, default both |
||||||
837 | * @return array of account_id's or null if none specified |
||||||
838 | * @throws Api\Exception\WrongUserinput(lang("Unknown account: %1 !!!",$account), 15); |
||||||
839 | * @throws Api\Exception\WrongUserinput(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only?lang('user'):lang('group')), 16); |
||||||
840 | */ |
||||||
841 | static function parse_accounts($accounts,$allow_only_user=null) |
||||||
842 | { |
||||||
843 | if (!$accounts) return null; |
||||||
844 | |||||||
845 | $ids = array(); |
||||||
846 | foreach(is_array($accounts) ? $accounts : explode(',',$accounts) as $account) |
||||||
847 | { |
||||||
848 | $ids[] = admin_cmd::parse_account($account,$allow_only_user); |
||||||
849 | } |
||||||
850 | return $ids; |
||||||
851 | } |
||||||
852 | |||||||
853 | /** |
||||||
854 | * Parses a date into an integer timestamp |
||||||
855 | * |
||||||
856 | * @param string $date |
||||||
857 | * @return int timestamp |
||||||
858 | * @throws Api\Exception\WrongUserinput(lang('Invalid formated date "%1"!',$datein),6); |
||||||
859 | */ |
||||||
860 | static function parse_date($date) |
||||||
861 | { |
||||||
862 | if (!is_numeric($date)) // we allow to input a timestamp |
||||||
863 | { |
||||||
864 | $datein = $date; |
||||||
865 | // convert german DD.MM.YYYY format into ISO YYYY-MM-DD format |
||||||
866 | $date = preg_replace('/^([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{4})$/','\3-\2-\1',$date); |
||||||
867 | |||||||
868 | if (($date = strtotime($date)) === false) |
||||||
869 | { |
||||||
870 | throw new Api\Exception\WrongUserinput(lang('Invalid formated date "%1"!',$datein),6); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $datein .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
871 | } |
||||||
872 | } |
||||||
873 | return (int)$date; |
||||||
874 | } |
||||||
875 | |||||||
876 | /** |
||||||
877 | * Parse a boolean value |
||||||
878 | * |
||||||
879 | * @param string|boolean|int $value |
||||||
880 | * @param boolean $default =null |
||||||
881 | * @return boolean |
||||||
882 | * @throws Api\Exception\WrongUserinput(lang('Invalid value "%1" use yes or no!',$value),998); |
||||||
883 | */ |
||||||
884 | static function parse_boolean($value,$default=null) |
||||||
885 | { |
||||||
886 | if (is_bool($value) || is_int($value)) |
||||||
887 | { |
||||||
888 | return (boolean)$value; |
||||||
889 | } |
||||||
890 | if (is_null($value) || (string)$value === '') |
||||||
891 | { |
||||||
892 | return $default; |
||||||
893 | } |
||||||
894 | if (in_array($value,array('1','yes','true',lang('yes'),lang('true')))) |
||||||
895 | { |
||||||
896 | return true; |
||||||
897 | } |
||||||
898 | if (in_array($value,array('0','no','false',lang('no'),lang('false')))) |
||||||
899 | { |
||||||
900 | return false; |
||||||
901 | } |
||||||
902 | throw new Api\Exception\WrongUserinput(lang('Invalid value "%1" use yes or no!',$value),998); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $value .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
903 | } |
||||||
904 | |||||||
905 | /** |
||||||
906 | * Parse a remote id or name and return the remote_id |
||||||
907 | * |
||||||
908 | * @param string $id_or_name |
||||||
909 | * @return int remote_id |
||||||
910 | * @throws Api\Exception\WrongUserinput(lang('Invalid remote id or name "%1"!',$id_or_name),997); |
||||||
911 | */ |
||||||
912 | static function parse_remote($id_or_name) |
||||||
913 | { |
||||||
914 | admin_cmd::_instanciate_remote(); |
||||||
915 | |||||||
916 | if (!($remotes = admin_cmd::$remote->search(array( |
||||||
917 | 'remote_id' => $id_or_name, |
||||||
918 | 'remote_name' => $id_or_name, |
||||||
919 | 'remote_domain' => $id_or_name, |
||||||
920 | ),true,'','','',false,'OR')) || count($remotes) != 1) |
||||||
921 | { |
||||||
922 | throw new Api\Exception\WrongUserinput(lang('Invalid remote id or name "%1"!',$id_or_name),997); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $id_or_name .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
923 | } |
||||||
924 | return $remotes[0]['remote_id']; |
||||||
925 | } |
||||||
926 | |||||||
927 | /** |
||||||
928 | * Instanciated Api\Accounts class |
||||||
929 | * |
||||||
930 | * @todo Api\Accounts class instanciation for setup |
||||||
931 | * @throws Api\Exception\AssertionFailed(lang('%1 class not instanciated','accounts'),999); |
||||||
932 | */ |
||||||
933 | static function _instanciate_accounts() |
||||||
934 | { |
||||||
935 | if (!is_object(admin_cmd::$accounts)) |
||||||
936 | { |
||||||
937 | if (!is_object($GLOBALS['egw']->accounts)) |
||||||
938 | { |
||||||
939 | throw new Api\Exception\AssertionFailed(lang('%1 class not instanciated','accounts'),999); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with 'accounts' .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
940 | } |
||||||
941 | admin_cmd::$accounts = $GLOBALS['egw']->accounts; |
||||||
942 | } |
||||||
943 | } |
||||||
944 | |||||||
945 | /** |
||||||
946 | * Instanciated Acl class |
||||||
947 | * |
||||||
948 | * @todo Acl class instanciation for setup |
||||||
949 | * @param int $account =null account_id the class needs to be instanciated for, default need only account-independent methods |
||||||
950 | * @throws Api\Exception\AssertionFailed(lang('%1 class not instanciated','acl'),999); |
||||||
951 | */ |
||||||
952 | protected function _instanciate_acl($account=null) |
||||||
953 | { |
||||||
954 | if (!is_object(admin_cmd::$acl) || $account && admin_cmd::$acl->account_id != $account) |
||||||
0 ignored issues
–
show
The expression
$account of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||||
955 | { |
||||||
956 | if (!is_object($GLOBALS['egw']->acl)) |
||||||
957 | { |
||||||
958 | throw new Api\Exception\AssertionFailed(lang('%1 class not instanciated','acl'),999); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with 'acl' .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
959 | } |
||||||
960 | if ($account && $GLOBALS['egw']->acl->account_id != $account) |
||||||
0 ignored issues
–
show
The expression
$account of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||||
961 | { |
||||||
962 | admin_cmd::$acl = new Acl($account); |
||||||
963 | admin_cmd::$acl->read_repository(); |
||||||
964 | } |
||||||
965 | else |
||||||
966 | { |
||||||
967 | admin_cmd::$acl = $GLOBALS['egw']->acl; |
||||||
968 | } |
||||||
969 | } |
||||||
970 | } |
||||||
971 | |||||||
972 | /** |
||||||
973 | * RFC822 email address of the an account, eg. "Ralf Becker <[email protected]>" |
||||||
974 | * |
||||||
975 | * @param $account_id =null account_id, default current user |
||||||
0 ignored issues
–
show
|
|||||||
976 | * @return string |
||||||
977 | */ |
||||||
978 | static function user_email($account_id=null) |
||||||
979 | { |
||||||
980 | if ($account_id) |
||||||
981 | { |
||||||
982 | admin_cmd::_instanciate_accounts(); |
||||||
983 | $fullname = admin_cmd::$accounts->id2name($account_id,'account_fullname'); |
||||||
984 | $email = admin_cmd::$accounts->id2name($account_id,'account_email'); |
||||||
985 | } |
||||||
986 | else |
||||||
987 | { |
||||||
988 | $fullname = $GLOBALS['egw_info']['user']['account_fullname']; |
||||||
989 | $email = $GLOBALS['egw_info']['user']['account_email']; |
||||||
990 | } |
||||||
991 | return $fullname . ($email ? ' <'.$email.'>' : ''); |
||||||
992 | } |
||||||
993 | |||||||
994 | /** |
||||||
995 | * Semaphore to not permanently set new jobs, while we running the current ones |
||||||
996 | * |
||||||
997 | * @var boolean |
||||||
998 | */ |
||||||
999 | private static $running_queued_jobs = false; |
||||||
1000 | const async_job_id = 'admin-command-queue'; |
||||||
0 ignored issues
–
show
|
|||||||
1001 | |||||||
1002 | /** |
||||||
1003 | * Setup an async job to run the next scheduled command |
||||||
1004 | * |
||||||
1005 | * Only needs to be called if a new command gets scheduled |
||||||
1006 | * |
||||||
1007 | * @return boolean true if job installed, false if not necessary |
||||||
1008 | */ |
||||||
1009 | private static function _set_async_job() |
||||||
1010 | { |
||||||
1011 | if (admin_cmd::$running_queued_jobs) |
||||||
1012 | { |
||||||
1013 | return false; |
||||||
1014 | } |
||||||
1015 | if (!($jobs = admin_cmd::search(array(),false,'cmd_scheduled','','',false,'AND',array(0,1),array( |
||||||
1016 | 'cmd_status' => admin_cmd::scheduled, |
||||||
1017 | )))) |
||||||
1018 | { |
||||||
1019 | return false; // no schduled command, no need to setup the job |
||||||
1020 | } |
||||||
1021 | $next = $jobs[0]; |
||||||
1022 | if (($time = $next['scheduled']) < time()) // should run immediatly |
||||||
1023 | { |
||||||
1024 | return admin_cmd::run_queued_jobs(); |
||||||
1025 | } |
||||||
1026 | $async = new Api\Asyncservice(); |
||||||
1027 | |||||||
1028 | // we cant use this class as callback, as it's abstract and ExecMethod used by the async service instanciated the class! |
||||||
1029 | list($app) = explode('_',$class=$next['type']); |
||||||
1030 | $callback = $app.'.'.$class.'.run_queued_jobs'; |
||||||
1031 | |||||||
1032 | $async->cancel_timer(admin_cmd::async_job_id); // we delete it in case a job already exists |
||||||
1033 | return $async->set_timer($time,admin_cmd::async_job_id,$callback,null,$next['creator']); |
||||||
1034 | } |
||||||
1035 | |||||||
1036 | /** |
||||||
1037 | * Callback for our async job |
||||||
1038 | * |
||||||
1039 | * @return boolean true if new job got installed, false if not necessary |
||||||
1040 | */ |
||||||
1041 | static function run_queued_jobs() |
||||||
1042 | { |
||||||
1043 | if (!($jobs = admin_cmd::search(array(),false,'cmd_scheduled','','',false,'AND',false,array( |
||||||
1044 | 'cmd_status' => admin_cmd::scheduled, |
||||||
1045 | 'cmd_scheduled <= '.time(), |
||||||
1046 | )))) |
||||||
1047 | { |
||||||
1048 | return false; // no schduled commands, no need to setup the job |
||||||
1049 | } |
||||||
1050 | admin_cmd::$running_queued_jobs = true; // stop admin_cmd::run() which calls admin_cmd::save() to install a new job |
||||||
1051 | |||||||
1052 | foreach($jobs as $job) |
||||||
1053 | { |
||||||
1054 | try { |
||||||
1055 | $cmd = admin_cmd::instanciate($job); |
||||||
1056 | $cmd->run(null,false); // false = dont set current user as modifier, as job is run by the queue/system itself |
||||||
1057 | } |
||||||
1058 | catch (Exception $e) { // we need to mark that command as failed, to prevent further execution |
||||||
1059 | _egw_log_exception($e); |
||||||
1060 | admin_cmd::$sql->init($job); |
||||||
1061 | admin_cmd::$sql->save(array( |
||||||
1062 | 'status' => admin_cmd::failed, |
||||||
1063 | 'error' => $e->getMessage(), |
||||||
1064 | 'errno' => $e->getcode(), |
||||||
1065 | 'data' => self::mask_passwords($job['data']), |
||||||
1066 | )); |
||||||
1067 | } |
||||||
1068 | } |
||||||
1069 | admin_cmd::$running_queued_jobs = false; |
||||||
1070 | |||||||
1071 | return admin_cmd::_set_async_job(); |
||||||
1072 | } |
||||||
1073 | |||||||
1074 | const PERIOD_ASYNC_ID_PREFIX = 'admin-cmd-'; |
||||||
1075 | |||||||
1076 | /** |
||||||
1077 | * Schedule next execution of a periodic job |
||||||
1078 | * |
||||||
1079 | * @return boolean |
||||||
1080 | */ |
||||||
1081 | public function set_periodic_job() |
||||||
1082 | { |
||||||
1083 | if (empty($this->rrule)) return false; |
||||||
1084 | |||||||
1085 | // parse rrule and calculate next execution time |
||||||
1086 | $event = calendar_rrule::parseRrule($this->rrule, true); // true: allow HOURLY or MINUTELY |
||||||
1087 | // rrule can depend on start-time, use policy creation time by default, if rrule_start is not set |
||||||
1088 | $event['start'] = empty($this->rrule_start) ? $this->created : $this->rrule_start; |
||||||
1089 | $event['tzid'] = Api\DateTime::$server_timezone->getName(); |
||||||
1090 | $rrule = calendar_rrule::event2rrule($event, false); // false = server timezone |
||||||
1091 | $rrule->rewind(); |
||||||
1092 | while((($time = $rrule->current()->format('ts'))) <= time()) |
||||||
1093 | { |
||||||
1094 | $rrule->next(); |
||||||
1095 | } |
||||||
1096 | |||||||
1097 | // schedule run_periodic_job to run at that time |
||||||
1098 | $async = new Api\Asyncservice(); |
||||||
1099 | $job_id = empty($this->async_job_id) ? self::PERIOD_ASYNC_ID_PREFIX.$this->id : $this->async_job_id; |
||||||
1100 | $async->cancel_timer($job_id); // we delete it in case a job already exists |
||||||
1101 | return $async->set_timer($time, $job_id, __CLASS__.'::run_periodic_job', $this->as_array(), $this->creator); |
||||||
1102 | } |
||||||
1103 | |||||||
1104 | /** |
||||||
1105 | * Cancel evtl. existing periodic job |
||||||
1106 | * |
||||||
1107 | * @return boolean true if job was canceled, false otherwise |
||||||
1108 | */ |
||||||
1109 | public function cancel_periodic_job() |
||||||
1110 | { |
||||||
1111 | $async = new Api\Asyncservice(); |
||||||
1112 | $job_id = empty($this->async_job_id) ? self::PERIOD_ASYNC_ID_PREFIX.$this->id : $this->async_job_id; |
||||||
1113 | $async->cancel_timer($job_id); // we delete it in case a job already exists |
||||||
1114 | } |
||||||
1115 | |||||||
1116 | /** |
||||||
1117 | * Run a periodic job, record it's result and schedule next run |
||||||
1118 | */ |
||||||
1119 | static function run_periodic_job($data) |
||||||
1120 | { |
||||||
1121 | $cmd = admin_cmd::read($data['id']); |
||||||
1122 | |||||||
1123 | // schedule next execution |
||||||
1124 | $cmd->set_periodic_job(); |
||||||
1125 | |||||||
1126 | // instanciate single periodic execution object |
||||||
1127 | $single = $cmd->as_array(); |
||||||
1128 | $single['parent'] = $single['id']; |
||||||
1129 | $args = array_diff_key($single, array_flip(array( |
||||||
1130 | 'id','uid', |
||||||
1131 | 'created','modified','modifier', |
||||||
1132 | 'async_job_id','rrule','scheduled', |
||||||
1133 | 'status', 'set', 'old','value','result' |
||||||
1134 | ))); |
||||||
1135 | |||||||
1136 | $periodic = admin_cmd::instanciate($args); |
||||||
1137 | |||||||
1138 | try { |
||||||
1139 | $value = $periodic->run(null, false); |
||||||
1140 | } |
||||||
1141 | catch (Exception $ex) { |
||||||
1142 | _egw_log_exception($ex); |
||||||
1143 | error_log(__METHOD__."(".array2string($data).") periodic execution failed: ".$ex->getMessage()); |
||||||
1144 | } |
||||||
1145 | $periodic->result = $value; |
||||||
1146 | $periodic->save(); |
||||||
1147 | } |
||||||
1148 | |||||||
1149 | /** |
||||||
1150 | * Return a list of defined remote instances |
||||||
1151 | * |
||||||
1152 | * @return array remote_id => remote_name pairs, plus 0 => local |
||||||
1153 | */ |
||||||
1154 | static function remote_sites() |
||||||
1155 | { |
||||||
1156 | admin_cmd::_instanciate_remote(); |
||||||
1157 | |||||||
1158 | $sites = array(lang('local')); |
||||||
1159 | if (($remote = admin_cmd::$remote->query_list('remote_name','remote_id'))) |
||||||
1160 | { |
||||||
1161 | $sites = array_merge($sites,$remote); |
||||||
1162 | } |
||||||
1163 | return $sites; |
||||||
1164 | } |
||||||
1165 | |||||||
1166 | /** |
||||||
1167 | * get_rows for remote instances |
||||||
1168 | * |
||||||
1169 | * @param array $query |
||||||
1170 | * @param array &$rows |
||||||
1171 | * @param array &$readonlys |
||||||
1172 | * @return int |
||||||
1173 | */ |
||||||
1174 | static function get_remotes($query,&$rows,&$readonlys) |
||||||
1175 | { |
||||||
1176 | admin_cmd::_instanciate_remote(); |
||||||
1177 | |||||||
1178 | return admin_cmd::$remote->get_rows($query,$rows,$readonlys); |
||||||
1179 | } |
||||||
1180 | |||||||
1181 | /** |
||||||
1182 | * Read data of a remote instance |
||||||
1183 | * |
||||||
1184 | * @param array|int $keys |
||||||
1185 | * @return array |
||||||
1186 | */ |
||||||
1187 | static function read_remote($keys) |
||||||
1188 | { |
||||||
1189 | admin_cmd::_instanciate_remote(); |
||||||
1190 | |||||||
1191 | return admin_cmd::$remote->read($keys); |
||||||
1192 | } |
||||||
1193 | |||||||
1194 | /** |
||||||
1195 | * Save / adds a remote instance |
||||||
1196 | * |
||||||
1197 | * @param array $data |
||||||
1198 | * @return int remote_id |
||||||
1199 | */ |
||||||
1200 | static function save_remote(array $data) |
||||||
1201 | { |
||||||
1202 | admin_cmd::_instanciate_remote(); |
||||||
1203 | |||||||
1204 | if ($data['install_id'] && $data['config_passwd']) // calculate hash |
||||||
1205 | { |
||||||
1206 | $data['remote_hash'] = self::remote_hash($data['install_id'],$data['config_passwd']); |
||||||
1207 | } |
||||||
1208 | elseif (!$data['remote_hash'] && !($data['install_id'] && $data['config_passwd'])) |
||||||
1209 | { |
||||||
1210 | throw new Api\Exception\WrongUserinput(lang('Either Install ID AND config password needed OR the remote hash!')); |
||||||
1211 | } |
||||||
1212 | //_debug_array($data); |
||||||
1213 | admin_cmd::$remote->init($data); |
||||||
1214 | |||||||
1215 | // check if a unique key constrain would be violated by saving the entry |
||||||
1216 | if (($num = admin_cmd::$remote->not_unique())) |
||||||
1217 | { |
||||||
1218 | $col = admin_cmd::$remote->table_def['uc'][$num-1]; // $num is 1 based! |
||||||
1219 | throw new egw_exception_db_not_unique(lang('Value for column %1 is not unique!',$this->table_name.'.'.$col),$num); |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $this->table_name . '.' . $col .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() The property
table_name does not exist on admin_cmd . Since you implemented __get , consider adding a @property annotation.
![]() Comprehensibility
Best Practice
introduced
by
The type
egw_exception_db_not_unique was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
1220 | } |
||||||
1221 | if (admin_cmd::$remote->save() != 0) |
||||||
1222 | { |
||||||
1223 | throw new Api\Db\Exception(lang('Error saving to db:').' '.$this->sql->db->Error.' ('.$this->sql->db->Errno.')',$this->sql->db->Errno); |
||||||
0 ignored issues
–
show
|
|||||||
1224 | } |
||||||
1225 | return admin_cmd::$remote->data['remote_id']; |
||||||
1226 | } |
||||||
1227 | |||||||
1228 | /** |
||||||
1229 | * Calculate the remote hash from install_id and config_passwd |
||||||
1230 | * |
||||||
1231 | * @param string $install_id |
||||||
1232 | * @param string $config_passwd |
||||||
1233 | * @return string 32char md5 hash |
||||||
1234 | */ |
||||||
1235 | static function remote_hash($install_id,$config_passwd) |
||||||
1236 | { |
||||||
1237 | if (empty($config_passwd) || !self::is_md5($install_id)) |
||||||
1238 | { |
||||||
1239 | throw new Api\Exception\WrongParameter(empty($config_passwd)?'Empty Api\Config password':'install_id no md5 hash'); |
||||||
1240 | } |
||||||
1241 | if (!self::is_md5($config_passwd)) $config_passwd = md5($config_passwd); |
||||||
1242 | |||||||
1243 | return md5($config_passwd.$install_id); |
||||||
1244 | } |
||||||
1245 | |||||||
1246 | /** |
||||||
1247 | * displays an account specified by it's id or lid |
||||||
1248 | * |
||||||
1249 | * We show the value given by the user, plus the full name in brackets. |
||||||
1250 | * |
||||||
1251 | * @param int|string $account |
||||||
1252 | * @return string |
||||||
1253 | */ |
||||||
1254 | static function display_account($account) |
||||||
1255 | { |
||||||
1256 | $id = is_numeric($account) ? $account : $GLOBALS['egw']->accounts->id2name($account); |
||||||
1257 | |||||||
1258 | return $account.' ('.Api\Accounts::username($id).')'; |
||||||
1259 | } |
||||||
1260 | |||||||
1261 | /** |
||||||
1262 | * Check if string is a md5 hash (32 chars of 0-9 or a-f) |
||||||
1263 | * |
||||||
1264 | * @param string $str |
||||||
1265 | * @return boolean |
||||||
1266 | */ |
||||||
1267 | static function is_md5($str) |
||||||
1268 | { |
||||||
1269 | return preg_match('/^[0-9a-f]{32}$/',$str); |
||||||
1270 | } |
||||||
1271 | |||||||
1272 | /** |
||||||
1273 | * Check if the current command has the right crediential to be excuted remotely |
||||||
1274 | * |
||||||
1275 | * Command can reimplement that method, to allow eg. anonymous execution. |
||||||
1276 | * |
||||||
1277 | * This default implementation use a secret to authenticate with the installation, |
||||||
1278 | * which is a md5 hash build from the uid of the command (to not allow to send new |
||||||
1279 | * commands with an earsdroped secret) and the md5 hash of the md5 hash of the |
||||||
1280 | * Api\Config password and the install_id (egw_admin_remote.remote_hash) |
||||||
1281 | * |
||||||
1282 | * @param string $secret hash used to authenticate the command ( |
||||||
1283 | * @param string $config_passwd of the current domain |
||||||
1284 | * @throws Api\Exception\NoPermission |
||||||
1285 | */ |
||||||
1286 | function check_remote_access($secret,$config_passwd) |
||||||
1287 | { |
||||||
1288 | // as a security measure remote administration need to be enabled under Admin > Site configuration |
||||||
1289 | list(,$remote_admin_install_id) = explode('-',$this->uid); |
||||||
1290 | $allowed_remote_admin_ids = $GLOBALS['egw_info']['server']['allow_remote_admin'] ? explode(',',$GLOBALS['egw_info']['server']['allow_remote_admin']) : array(); |
||||||
1291 | |||||||
1292 | // to authenticate with the installation we use a secret, which is a md5 hash build from the uid |
||||||
1293 | // of the command (to not allow to send new commands with an earsdroped secret) and the md5 hash |
||||||
1294 | // of the md5 hash of the Api\Config password and the install_id (egw_admin_remote.remote_hash) |
||||||
1295 | if (is_null($config_passwd) || is_numeric($this->uid) || !in_array($remote_admin_install_id,$allowed_remote_admin_ids) || |
||||||
1296 | $secret != ($md5=md5($this->uid.$this->remote_hash($GLOBALS['egw_info']['server']['install_id'],$config_passwd)))) |
||||||
1297 | { |
||||||
1298 | //die("secret='$secret' != '$md5', is_null($config_passwd)=".is_null($config_passwd).", uid=$this->uid, remote_install_id=$remote_admin_install_id, allowed: ".implode(', ',$allowed_remote_admin_ids)); |
||||||
1299 | unset($md5); |
||||||
1300 | $msg = lang('Permission denied!'); |
||||||
1301 | if (!in_array($remote_admin_install_id,$allowed_remote_admin_ids)) |
||||||
1302 | { |
||||||
1303 | $msg .= "\n".lang('Remote administration need to be enabled in the remote instance under Admin > Site configuration!'); |
||||||
1304 | } |
||||||
1305 | throw new Api\Exception\NoPermission($msg,0); |
||||||
1306 | } |
||||||
1307 | } |
||||||
1308 | |||||||
1309 | /** |
||||||
1310 | * Return a rand string, eg. to generate passwords |
||||||
1311 | * |
||||||
1312 | * @param int $len =16 |
||||||
1313 | * @return string |
||||||
1314 | */ |
||||||
1315 | static function randomstring($len=16) |
||||||
1316 | { |
||||||
1317 | return Api\Auth::randomstring($len); |
||||||
1318 | } |
||||||
1319 | |||||||
1320 | /** |
||||||
1321 | * Get name of eTemplate used to make the change to derive UI for history |
||||||
1322 | * |
||||||
1323 | * @return string|null etemplate name |
||||||
1324 | */ |
||||||
1325 | protected function get_etemplate_name() |
||||||
1326 | { |
||||||
1327 | return null; |
||||||
1328 | } |
||||||
1329 | |||||||
1330 | /** |
||||||
1331 | * Return eTemplate used to make the change to derive UI for history |
||||||
1332 | * |
||||||
1333 | * @return Api\Etemplate|null |
||||||
1334 | */ |
||||||
1335 | protected function get_etemplate() |
||||||
1336 | { |
||||||
1337 | static $tpl = null; // some caching to not instanciate it twice |
||||||
1338 | |||||||
1339 | if (!isset($tpl)) |
||||||
1340 | { |
||||||
1341 | $name = $this->get_etemplate_name(); |
||||||
0 ignored issues
–
show
Are you sure the assignment to
$name is correct as $this->get_etemplate_name() targeting admin_cmd::get_etemplate_name() seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||
1342 | if (empty($name)) |
||||||
1343 | { |
||||||
1344 | $tpl = false; |
||||||
1345 | } |
||||||
1346 | else |
||||||
1347 | { |
||||||
1348 | $tpl = Api\Etemplate::instance($name); |
||||||
1349 | Api\Etemplate::reset_request(); |
||||||
1350 | } |
||||||
1351 | } |
||||||
1352 | return $tpl ? $tpl : null; |
||||||
1353 | } |
||||||
1354 | |||||||
1355 | /** |
||||||
1356 | * Return (human readable) labels for keys of changes |
||||||
1357 | * |
||||||
1358 | * @return array |
||||||
1359 | */ |
||||||
1360 | function get_change_labels() |
||||||
1361 | { |
||||||
1362 | $labels = []; |
||||||
1363 | $label = null; |
||||||
1364 | if (($tpl = $this->get_etemplate())) |
||||||
1365 | { |
||||||
1366 | $tpl->run(function($cname, $expand, $widget) use (&$labels, &$label) |
||||||
1367 | { |
||||||
1368 | switch($widget->type) |
||||||
1369 | { |
||||||
1370 | // remember label from last description widget |
||||||
1371 | case 'description': |
||||||
1372 | if (!empty($widget->attrs['value'])) $label = $widget->attrs['value']; |
||||||
1373 | break; |
||||||
1374 | // ignore non input-widgets |
||||||
1375 | case 'hbox': case 'vbox': case 'box': case 'groupbox': |
||||||
1376 | case 'grid': case 'columns': case 'column': case 'rows': case 'row': |
||||||
1377 | case 'template': case 'tabbox': case 'tabs': case 'tab': |
||||||
1378 | // ignore buttons too |
||||||
1379 | case 'button': case 'buttononly': |
||||||
1380 | break; |
||||||
1381 | default: |
||||||
1382 | if (!empty($widget->id)) |
||||||
1383 | { |
||||||
1384 | if (!empty($widget->attrs['label'])) $label = $widget->attrs['label']; |
||||||
1385 | if (!empty($label)) $labels[$widget->id] = $label; |
||||||
1386 | $label = null; |
||||||
1387 | } |
||||||
1388 | break; |
||||||
1389 | } |
||||||
1390 | unset($cname, $expand); |
||||||
1391 | }, ['', []]); |
||||||
1392 | } |
||||||
1393 | return $labels; |
||||||
1394 | } |
||||||
1395 | |||||||
1396 | /** |
||||||
1397 | * Return widget types (indexed by field key) for changes |
||||||
1398 | * |
||||||
1399 | * Used by historylog widget to show the changes the command recorded. |
||||||
1400 | */ |
||||||
1401 | function get_change_widgets() |
||||||
1402 | { |
||||||
1403 | static $selectboxes = ['select', 'listbox', 'menupopup', 'taglist']; |
||||||
1404 | |||||||
1405 | $widgets = []; |
||||||
1406 | $last_select = null; |
||||||
1407 | if (($tpl = $this->get_etemplate())) |
||||||
1408 | { |
||||||
1409 | $tpl->run(function($cname, $expand, $widget) use (&$widgets, &$last_select, $selectboxes) |
||||||
1410 | { |
||||||
1411 | switch($widget->type) |
||||||
1412 | { |
||||||
1413 | // ignore non input-widgets |
||||||
1414 | case 'hbox': case 'vbox': case 'box': case 'groupbox': |
||||||
1415 | case 'grid': case 'columns': case 'column': case 'rows': case 'row': |
||||||
1416 | case 'template': case 'tabbox': case 'tabs': case 'tab': |
||||||
1417 | // No need for these |
||||||
1418 | case 'textbox': case 'int': case 'float': |
||||||
1419 | // ignore widgets that can't go in the historylog |
||||||
1420 | case 'button': case 'buttononly': case 'taglist-thumbnail': |
||||||
1421 | break; |
||||||
1422 | case 'radio': |
||||||
1423 | if (!is_array($widgets[$widget->id])) $widgets[$widget->id] = []; |
||||||
1424 | $label = (string)$widget->attrs['label']; |
||||||
1425 | // translate "{something} {else}" type options |
||||||
1426 | if (strpos($label, '{') !== false) |
||||||
1427 | { |
||||||
1428 | $label = preg_replace_callback('/{([^}]+)}/', function($matches) |
||||||
1429 | { |
||||||
1430 | return lang($matches[1]); |
||||||
1431 | }, $label); |
||||||
1432 | } |
||||||
1433 | $widgets[$widget->id][(string)$widget->attrs['set_value']] = $label; |
||||||
1434 | break; |
||||||
1435 | // config templates have options in the template |
||||||
1436 | case 'option': |
||||||
1437 | if (!is_array($widgets[$last_select])) $widgets[$last_select] = []; |
||||||
1438 | $label = (string)$widget->attrs['#text']; |
||||||
1439 | // translate "{something} {else}" type options |
||||||
1440 | if (strpos($label, '{') !== false) |
||||||
1441 | { |
||||||
1442 | $label = preg_replace_callback('/{([^}]+)}/', function($matches) |
||||||
1443 | { |
||||||
1444 | return lang($matches[1]); |
||||||
1445 | }, $label); |
||||||
1446 | } |
||||||
1447 | $widgets[$last_select][(string)$widget->attrs['value']] = $label; |
||||||
1448 | break; |
||||||
1449 | default: |
||||||
1450 | $last_select = null; |
||||||
1451 | if (!empty($widget->id)) |
||||||
1452 | { |
||||||
1453 | $widgets[$widget->id] = $widget->type; |
||||||
1454 | if (in_array($widget->type, $selectboxes)) |
||||||
1455 | { |
||||||
1456 | $last_select = $widget->id; |
||||||
1457 | } |
||||||
1458 | } |
||||||
1459 | break; |
||||||
1460 | } |
||||||
1461 | unset($cname, $expand); |
||||||
1462 | }, ['', []]); |
||||||
1463 | |||||||
1464 | // remove pure selectboxes, as they would show nothing without having options |
||||||
1465 | $widgets = array_diff($widgets, $selectboxes); |
||||||
1466 | } |
||||||
1467 | return $widgets; |
||||||
1468 | } |
||||||
1469 | |||||||
1470 | /** |
||||||
1471 | * Get the result of executing the command. |
||||||
1472 | * Should be some kind of success or results message indicating what was done. |
||||||
1473 | */ |
||||||
1474 | public function get_result() |
||||||
1475 | { |
||||||
1476 | if($this->result) |
||||||
1477 | { |
||||||
1478 | return is_array($this->result) ? implode("\n", $this->result) : $this->result; |
||||||
1479 | } |
||||||
1480 | return lang("Command was run %1 on %2", |
||||||
1481 | static::$stati[ $this->status ], |
||||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with static::stati[$this->status] .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
1482 | Api\DateTime::to($this->created)); |
||||||
1483 | } |
||||||
1484 | } |
||||||
1485 |