option('debug'), 'log' => option('bnomei.janitor.log.fn'), 'jobs' => option('bnomei.janitor.jobs'), 'jobs-defaults' => ['bnomei.janitor.jobs-defaults'], 'jobs-extends' => option('bnomei.janitor.jobs-extends'), 'secret' => option('bnomei.janitor.secret'), ]; $this->options = array_merge($defaults, $options); $extends = array_merge($this->options['jobs-defaults'], $this->options['jobs-extends']); foreach ($extends as $extend) { // NOTE: it is intended that jobs override merged not other way around $this->options['jobs'] = array_change_key_case( array_merge(option($extend, []), $this->options['jobs']) ); } foreach ($this->options as $key => $call) { if (is_callable($call) && in_array($key, ['secret'])) { $this->options[$key] = $call(); } } } /** * @param string|null $key * @return array */ public function option(?string $key = null) { if ($key) { return A::get($this->options, $key); } return $this->options; } /** * @param string $secret * @param string $name * @param array $data * @return array */ public function jobWithSecret(string $secret, string $name, array $data = []): array { if ($secret === $this->option('secret')) { return $this->job($name, $data); } return [ 'status' => 401, ]; } /** * @param string $name * @param array $data * @return array */ public function job(string $name, array $data = []): array { $job = $this->findJob($name); if (!is_string($job) && is_callable($job)) { return $this->jobFromCallable($job, $data); } elseif (class_exists($job)) { return $this->jobFromClass($job, $data); } return [ 'status' => 404, ]; } /** * @return mixed */ public function listJobs() { // find in jobs config return array_keys($this->option('jobs')); } /** * @param string $name * @return mixed|string */ public function findJob(string $name) { // find in jobs config $jobInConfig = A::get($this->option('jobs'), strtolower($name)); if ($jobInConfig) { return $jobInConfig; } // could be a class return $name; } /** * @param $job * @param array $data * @return array */ public function jobFromCallable($job, array $data): array { $return = false; try { set_time_limit(0); } catch (\Exception $ex) { // ignore } try { $return = $job( page(str_replace('+', '/', urldecode(A::get($data, 'contextPage', '')))), str_replace('+S_L_A_S_H+', '/', urldecode(A::get($data, 'contextData', ''))) ); } catch (\BadMethodCallException $ex) { $return = $job(); } if (is_array($return)) { return $return; } return [ 'status' => $return ? 200 : 404, ]; } /** * @param string $job * @param array $data * @return array */ public function jobFromClass(string $job, array $data): array { $object = new $job( page(str_replace('+', '/', urldecode(A::get($data, 'contextPage', '')))), str_replace('+S_L_A_S_H+', '/', urldecode(A::get($data, 'contextData', ''))) ); if (method_exists($object, 'job')) { try { set_time_limit(0); } catch (\Exception $ex) { // ignore } return $object->job(); } return [ 'status' => 400, ]; } /** * @param string $msg * @param string $level * @param array $context * @return bool */ public function log(string $msg = '', string $level = 'info', array $context = []): bool { $log = $this->option('log'); if ($log && is_callable($log)) { if (!$this->option('debug') && $level == 'debug') { // skip but... return true; } else { return $log($msg, $level, $context); } } return false; } /* * @var Janitor */ private static $singleton; /** * @param array $options * @return Janitor */ public static function singleton(array $options = []): Janitor { if (self::$singleton) { return self::$singleton; } self::$singleton = new Janitor($options); return self::$singleton; } /** * @param string|null $template * @param mixed|null $model * @return string */ public static function query(string $template = null, $model = null): string { $page = null; $file = null; $user = kirby()->user(); if ($model && $model instanceof Page) { $page = $model; } elseif ($model && $model instanceof File) { $file = $model; } elseif ($model && $model instanceof User) { $user = $model; } return Str::template($template, [ 'kirby' => kirby(), 'site' => kirby()->site(), 'page' => $page, 'file' => $file, 'user' => $user, 'model' => $model ? get_class($model) : null, ]); } /** * @param $val * @param bool $return_null * @return bool */ public static function isTrue($val, $return_null = false): bool { $boolval = (is_string($val) ? filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : (bool) $val); $boolval = ($boolval === null && !$return_null ? false : $boolval); return $boolval; } /** * @var \League\CLImate\CLImate */ private static $climate; /** * @param CLImate|null $climate * @return CLImate|null */ public static function climate(?CLImate $climate = null): ?CLImate { if ($climate && !self::$climate) { self::$climate = $climate; } return self::$climate; } }