name = $name; $this->grav = $grav; if ($config) { $this->setConfig($config); } } /** * @return ClassLoader|null * @internal */ final public function getAutoloader(): ?ClassLoader { return $this->loader; } /** * @param ClassLoader|null $loader * @internal */ final public function setAutoloader(?ClassLoader $loader): void { $this->loader = $loader; } /** * @param Config $config * @return $this */ public function setConfig(Config $config) { $this->config = $config; return $this; } /** * Get configuration of the plugin. * * @return array */ public function config() { return $this->config["plugins.{$this->name}"] ?? []; } /** * Determine if plugin is running under the admin * * @return bool */ public function isAdmin() { return Utils::isAdminPlugin(); } /** * Determine if plugin is running under the CLI * * @return bool */ public function isCli() { return defined('GRAV_CLI'); } /** * Determine if this route is in Admin and active for the plugin * * @param string $plugin_route * @return bool */ protected function isPluginActiveAdmin($plugin_route) { $active = false; /** @var Uri $uri */ $uri = $this->grav['uri']; /** @var Config $config */ $config = $this->config ?? $this->grav['config']; if (strpos($uri->path(), $config->get('plugins.admin.route') . '/' . $plugin_route) === false) { $active = false; } elseif (isset($uri->paths()[1]) && $uri->paths()[1] === $plugin_route) { $active = true; } return $active; } /** * @param array $events * @return void */ protected function enable(array $events) { /** @var EventDispatcher $dispatcher */ $dispatcher = $this->grav['events']; foreach ($events as $eventName => $params) { if (is_string($params)) { $dispatcher->addListener($eventName, [$this, $params]); } elseif (is_string($params[0])) { $dispatcher->addListener($eventName, [$this, $params[0]], $this->getPriority($params, $eventName)); } else { foreach ($params as $listener) { $dispatcher->addListener($eventName, [$this, $listener[0]], $this->getPriority($listener, $eventName)); } } } } /** * @param array $params * @param string $eventName * @return int */ private function getPriority($params, $eventName) { $override = implode('.', ['priorities', $this->name, $eventName, $params[0]]); return $this->grav['config']->get($override) ?? $params[1] ?? 0; } /** * @param array $events * @return void */ protected function disable(array $events) { /** @var EventDispatcher $dispatcher */ $dispatcher = $this->grav['events']; foreach ($events as $eventName => $params) { if (is_string($params)) { $dispatcher->removeListener($eventName, [$this, $params]); } elseif (is_string($params[0])) { $dispatcher->removeListener($eventName, [$this, $params[0]]); } else { foreach ($params as $listener) { $dispatcher->removeListener($eventName, [$this, $listener[0]]); } } } } /** * Whether or not an offset exists. * * @param string $offset An offset to check for. * @return bool Returns TRUE on success or FALSE on failure. */ #[\ReturnTypeWillChange] public function offsetExists($offset) { if ($offset === 'title') { $offset = 'name'; } $blueprint = $this->getBlueprint(); return isset($blueprint[$offset]); } /** * Returns the value at specified offset. * * @param string $offset The offset to retrieve. * @return mixed Can return all value types. */ #[\ReturnTypeWillChange] public function offsetGet($offset) { if ($offset === 'title') { $offset = 'name'; } $blueprint = $this->getBlueprint(); return $blueprint[$offset] ?? null; } /** * Assigns a value to the specified offset. * * @param string $offset The offset to assign the value to. * @param mixed $value The value to set. * @throws LogicException */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { throw new LogicException(__CLASS__ . ' blueprints cannot be modified.'); } /** * Unsets an offset. * * @param string $offset The offset to unset. * @throws LogicException */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { throw new LogicException(__CLASS__ . ' blueprints cannot be modified.'); } /** * @return array */ public function __debugInfo(): array { $array = (array)$this; unset($array["\0*\0grav"]); $array["\0*\0config"] = $this->config(); return $array; } /** * This function will search a string for markdown links in a specific format. The link value can be * optionally compared against via the $internal_regex and operated on by the callback $function * provided. * * format: [plugin:myplugin_name](function_data) * * @param string $content The string to perform operations upon * @param callable $function The anonymous callback function * @param string $internal_regex Optional internal regex to extra data from * @return string */ protected function parseLinks($content, $function, $internal_regex = '(.*)') { $regex = '/\[plugin:(?:' . preg_quote($this->name, '/') . ')\]\(' . $internal_regex . '\)/i'; $result = preg_replace_callback($regex, $function, $content); \assert($result !== null); return $result; } /** * Merge global and page configurations. * * WARNING: This method modifies page header! * * @param PageInterface $page The page to merge the configurations with the * plugin settings. * @param mixed $deep false = shallow|true = recursive|merge = recursive+unique * @param array $params Array of additional configuration options to * merge with the plugin settings. * @param string $type Is this 'plugins' or 'themes' * @return Data */ protected function mergeConfig(PageInterface $page, $deep = false, $params = [], $type = 'plugins') { /** @var Config $config */ $config = $this->config ?? $this->grav['config']; $class_name = $this->name; $class_name_merged = $class_name . '.merged'; $defaults = $config->get($type . '.' . $class_name, []); $page_header = $page->header(); $header = []; if (!isset($page_header->{$class_name_merged}) && isset($page_header->{$class_name})) { // Get default plugin configurations and retrieve page header configuration $config = $page_header->{$class_name}; if (is_bool($config)) { // Overwrite enabled option with boolean value in page header $config = ['enabled' => $config]; } // Merge page header settings using deep or shallow merging technique $header = $this->mergeArrays($deep, $defaults, $config); // Create new config object and set it on the page object so it's cached for next time $page->modifyHeader($class_name_merged, new Data($header)); } elseif (isset($page_header->{$class_name_merged})) { $merged = $page_header->{$class_name_merged}; $header = $merged->toArray(); } if (empty($header)) { $header = $defaults; } // Merge additional parameter with configuration options $header = $this->mergeArrays($deep, $header, $params); // Return configurations as a new data config class return new Data($header); } /** * Merge arrays based on deepness * * @param string|bool $deep * @param array $array1 * @param array $array2 * @return array */ private function mergeArrays($deep, $array1, $array2) { if ($deep === 'merge') { return Utils::arrayMergeRecursiveUnique($array1, $array2); } if ($deep === true) { return array_replace_recursive($array1, $array2); } return array_merge($array1, $array2); } /** * Persists to disk the plugin parameters currently stored in the Grav Config object * * @param string $name The name of the plugin whose config it should store. * @return bool */ public static function saveConfig($name) { if (!$name) { return false; } $grav = Grav::instance(); /** @var UniformResourceLocator $locator */ $locator = $grav['locator']; $filename = 'config://plugins/' . $name . '.yaml'; $file = YamlFile::instance((string)$locator->findResource($filename, true, true)); $content = $grav['config']->get('plugins.' . $name); $file->save($content); $file->free(); unset($file); return true; } public static function inheritedConfigOption(string $plugin, string $var, PageInterface $page = null, $default = null) { if (Utils::isAdminPlugin()) { $page = Grav::instance()['admin']->page() ?? null; } else { $page = $page ?? Grav::instance()['page'] ?? null; } // Try to find var in the page headers if ($page instanceof PageInterface && $page->exists()) { // Loop over pages and look for header vars while ($page && !$page->root()) { $header = new Data((array)$page->header()); $value = $header->get("$plugin.$var"); if (isset($value)) { return $value; } $page = $page->parent(); } } return Grav::instance()['config']->get("plugins.$plugin.$var", $default); } /** * Simpler getter for the plugin blueprint * * @return Blueprint */ public function getBlueprint() { if (null === $this->blueprint) { $this->loadBlueprint(); \assert($this->blueprint instanceof Blueprint); } return $this->blueprint; } /** * Load blueprints. * * @return void */ protected function loadBlueprint() { if (null === $this->blueprint) { $grav = Grav::instance(); /** @var Plugins $plugins */ $plugins = $grav['plugins']; $data = $plugins->get($this->name); \assert($data !== null); $this->blueprint = $data->blueprints(); } } }