1f3281ad6SGreg Roach<?php 2f3281ad6SGreg Roach/** 3f3281ad6SGreg Roach * webtrees: online genealogy 48fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team 5f3281ad6SGreg Roach * This program is free software: you can redistribute it and/or modify 6f3281ad6SGreg Roach * it under the terms of the GNU General Public License as published by 7f3281ad6SGreg Roach * the Free Software Foundation, either version 3 of the License, or 8f3281ad6SGreg Roach * (at your option) any later version. 9f3281ad6SGreg Roach * This program is distributed in the hope that it will be useful, 10f3281ad6SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 11f3281ad6SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12f3281ad6SGreg Roach * GNU General Public License for more details. 13f3281ad6SGreg Roach * You should have received a copy of the GNU General Public License 14f3281ad6SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 15f3281ad6SGreg Roach */ 16e7f56f2aSGreg Roachdeclare(strict_types=1); 17e7f56f2aSGreg Roach 18f3281ad6SGreg Roachnamespace Fisharebest\Webtrees; 19f3281ad6SGreg Roach 2050c68a25SGreg Roachuse Exception; 218136679eSGreg Roachuse Fisharebest\Webtrees\Module\ModuleThemeInterface; 2270f31542SGreg Roachuse function ob_end_clean; 2370f31542SGreg Roachuse Throwable; 2450c68a25SGreg Roach 25f3281ad6SGreg Roach/** 26f3281ad6SGreg Roach * Simple view/template class. 27f3281ad6SGreg Roach */ 28c1010edaSGreg Roachclass View 29c1010edaSGreg Roach{ 30dd6b2bfcSGreg Roach // Where do our templates live 3116d6367aSGreg Roach private const TEMPLATE_PATH = 'resources/views/'; 32dd6b2bfcSGreg Roach 33dd6b2bfcSGreg Roach // File extension for our template files. 3416d6367aSGreg Roach private const TEMPLATE_EXTENSION = '.phtml'; 35dd6b2bfcSGreg Roach 36f3281ad6SGreg Roach /** 37f3281ad6SGreg Roach * @var string The (file) name of the view. 38f3281ad6SGreg Roach */ 39f3281ad6SGreg Roach private $name; 40f3281ad6SGreg Roach 41f3281ad6SGreg Roach /** 42f3281ad6SGreg Roach * @var mixed[] Data to be inserted into the view. 43f3281ad6SGreg Roach */ 44f3281ad6SGreg Roach private $data; 45f3281ad6SGreg Roach 46f3281ad6SGreg Roach /** 4708d24c7aSGreg Roach * @var mixed[] Data to be inserted into all views. 4808d24c7aSGreg Roach */ 4908d24c7aSGreg Roach private static $shared_data = []; 5008d24c7aSGreg Roach 5108d24c7aSGreg Roach /** 52ecf66805SGreg Roach * @var string Implementation of Blade "stacks". 53ecf66805SGreg Roach */ 54ecf66805SGreg Roach private static $stack; 55ecf66805SGreg Roach 56ecf66805SGreg Roach /** 57ecf66805SGreg Roach * @var array[] Implementation of Blade "stacks". 58ecf66805SGreg Roach */ 59ecf66805SGreg Roach private static $stacks = []; 60ecf66805SGreg Roach 61ecf66805SGreg Roach /** 62f3281ad6SGreg Roach * Createa view from a template name and optional data. 63f3281ad6SGreg Roach * 64fa4036e8SGreg Roach * @param string $name 65f3281ad6SGreg Roach * @param array $data 66f3281ad6SGreg Roach */ 67fa4036e8SGreg Roach public function __construct(string $name, $data = []) 68c1010edaSGreg Roach { 69f3281ad6SGreg Roach $this->name = $name; 70f3281ad6SGreg Roach $this->data = $data; 71f3281ad6SGreg Roach } 72f3281ad6SGreg Roach 73f3281ad6SGreg Roach /** 7408d24c7aSGreg Roach * Shared data that is available to all views. 75962e29c9SGreg Roach * 76962e29c9SGreg Roach * @param string $key 77962e29c9SGreg Roach * @param mixed $value 78fa4036e8SGreg Roach * 79fa4036e8SGreg Roach * @return void 8008d24c7aSGreg Roach */ 81c1010edaSGreg Roach public static function share(string $key, $value) 82c1010edaSGreg Roach { 8308d24c7aSGreg Roach self::$shared_data[$key] = $value; 8408d24c7aSGreg Roach } 8508d24c7aSGreg Roach 8608d24c7aSGreg Roach /** 87ecf66805SGreg Roach * Implementation of Blade "stacks". 88ecf66805SGreg Roach * 89ecf66805SGreg Roach * @see https://laravel.com/docs/5.5/blade#stacks 90962e29c9SGreg Roach * 91962e29c9SGreg Roach * @param string $stack 92fa4036e8SGreg Roach * 93fa4036e8SGreg Roach * @return void 94ecf66805SGreg Roach */ 95c1010edaSGreg Roach public static function push(string $stack) 96c1010edaSGreg Roach { 97ecf66805SGreg Roach self::$stack = $stack; 98ecf66805SGreg Roach ob_start(); 99ecf66805SGreg Roach } 100ecf66805SGreg Roach 101ecf66805SGreg Roach /** 102ecf66805SGreg Roach * Implementation of Blade "stacks". 103fa4036e8SGreg Roach * 104fa4036e8SGreg Roach * @return void 105ecf66805SGreg Roach */ 106c1010edaSGreg Roach public static function endpush() 107c1010edaSGreg Roach { 108*88de55fdSRico Sonntag $content = ob_get_clean(); 109*88de55fdSRico Sonntag $hash = sha1($content); 110*88de55fdSRico Sonntag 111*88de55fdSRico Sonntag self::$stacks[self::$stack][$hash] = $content; 112ecf66805SGreg Roach } 113ecf66805SGreg Roach 114ecf66805SGreg Roach /** 115ecf66805SGreg Roach * Implementation of Blade "stacks". 116ecf66805SGreg Roach * 117962e29c9SGreg Roach * @param string $stack 118962e29c9SGreg Roach * 119ecf66805SGreg Roach * @return string 120ecf66805SGreg Roach */ 121c1010edaSGreg Roach public static function stack(string $stack): string 122c1010edaSGreg Roach { 123ecf66805SGreg Roach $content = implode('', self::$stacks[$stack] ?? []); 124ecf66805SGreg Roach 125ecf66805SGreg Roach self::$stacks[$stack] = []; 126ecf66805SGreg Roach 127ecf66805SGreg Roach return $content; 128ecf66805SGreg Roach } 129ecf66805SGreg Roach 130ecf66805SGreg Roach /** 131f3281ad6SGreg Roach * Render a view. 132f3281ad6SGreg Roach * 133f3281ad6SGreg Roach * @return string 13470f31542SGreg Roach * @throws Throwable 135f3281ad6SGreg Roach */ 1368f53f488SRico Sonntag public function render(): string 137c1010edaSGreg Roach { 1384a86d714SGreg Roach $variables_for_view = $this->data + self::$shared_data; 1394a86d714SGreg Roach extract($variables_for_view); 140f3281ad6SGreg Roach 14170f31542SGreg Roach try { 142f3281ad6SGreg Roach ob_start(); 143bc241c54SGreg Roach // Do not use require, so we can catch errors for missing files 144bc241c54SGreg Roach include $this->getFilenameForView($this->name); 14575d70144SGreg Roach 146f3281ad6SGreg Roach return ob_get_clean(); 14770f31542SGreg Roach } catch (Throwable $ex) { 14870f31542SGreg Roach ob_end_clean(); 14970f31542SGreg Roach throw $ex; 15070f31542SGreg Roach } 151f3281ad6SGreg Roach } 152f3281ad6SGreg Roach 153f3281ad6SGreg Roach /** 15475d70144SGreg Roach * Allow a theme to override the default views. 155f3281ad6SGreg Roach * 156f3281ad6SGreg Roach * @param string $view_name 157f3281ad6SGreg Roach * 15875d70144SGreg Roach * @return string 15950c68a25SGreg Roach * @throws Exception 160f3281ad6SGreg Roach */ 1618f53f488SRico Sonntag public function getFilenameForView($view_name): string 162c1010edaSGreg Roach { 16350c68a25SGreg Roach foreach ($this->paths() as $path) { 164dd6b2bfcSGreg Roach $view_file = $path . $view_name . self::TEMPLATE_EXTENSION; 16575d70144SGreg Roach 16650c68a25SGreg Roach if (is_file($view_file)) { 16750c68a25SGreg Roach return $view_file; 168f21917b2SGreg Roach } 169f3281ad6SGreg Roach } 170f3281ad6SGreg Roach 17150c68a25SGreg Roach throw new Exception('View not found: ' . e($view_name)); 17250c68a25SGreg Roach } 17350c68a25SGreg Roach 174f3281ad6SGreg Roach /** 175f3281ad6SGreg Roach * Cerate and render a view in a single operation. 176f3281ad6SGreg Roach * 177f3281ad6SGreg Roach * @param string $name 178f3281ad6SGreg Roach * @param mixed[] $data 179f3281ad6SGreg Roach * 180f3281ad6SGreg Roach * @return string 181f3281ad6SGreg Roach */ 1828f53f488SRico Sonntag public static function make($name, $data = []): string 183c1010edaSGreg Roach { 184f3281ad6SGreg Roach $view = new static($name, $data); 185f3281ad6SGreg Roach 18644116e73SGreg Roach DebugBar::addView($name, $data); 18744116e73SGreg Roach 188f3281ad6SGreg Roach return $view->render(); 189f3281ad6SGreg Roach } 19050c68a25SGreg Roach 19150c68a25SGreg Roach /** 19250c68a25SGreg Roach * @return string[] 19350c68a25SGreg Roach */ 19450c68a25SGreg Roach private function paths(): array 19550c68a25SGreg Roach { 19650c68a25SGreg Roach static $paths = []; 19750c68a25SGreg Roach 19850c68a25SGreg Roach if (empty($paths)) { 19950c68a25SGreg Roach // Module views 20050c68a25SGreg Roach // @TODO - this includes disabled modules. 2018d0ebef0SGreg Roach $paths = glob(WT_ROOT . Webtrees::MODULES_PATH . '*/' . self::TEMPLATE_PATH); 20250c68a25SGreg Roach // Theme views 2038136679eSGreg Roach $paths[] = WT_ROOT . Webtrees::THEMES_PATH . app()->make(ModuleThemeInterface::class)->name() . '/' . self::TEMPLATE_PATH; 20450c68a25SGreg Roach // Core views 205dd6b2bfcSGreg Roach $paths[] = WT_ROOT . self::TEMPLATE_PATH; 20650c68a25SGreg Roach 207bdb3725aSGreg Roach $paths = array_filter($paths, function (string $path): bool { 208bdb3725aSGreg Roach return is_dir($path); 209bdb3725aSGreg Roach }); 21050c68a25SGreg Roach } 21150c68a25SGreg Roach 21250c68a25SGreg Roach return $paths; 21350c68a25SGreg Roach } 214f3281ad6SGreg Roach} 215