xref: /webtrees/app/View.php (revision d7952a34bc82db471ebc960cb25a37234ddd3514)
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;
2170f31542SGreg Roachuse Throwable;
22*d7952a34SGreg Roachuse function array_filter;
23*d7952a34SGreg Roachuse function extract;
24*d7952a34SGreg Roachuse function implode;
25*d7952a34SGreg Roachuse function is_dir;
26*d7952a34SGreg Roachuse function is_file;
27*d7952a34SGreg Roachuse function ob_end_clean;
28*d7952a34SGreg Roachuse function ob_start;
29*d7952a34SGreg Roachuse function sha1;
3050c68a25SGreg Roach
31f3281ad6SGreg Roach/**
32f3281ad6SGreg Roach * Simple view/template class.
33f3281ad6SGreg Roach */
34c1010edaSGreg Roachclass View
35c1010edaSGreg Roach{
36dd6b2bfcSGreg Roach    // Where do our templates live
3716d6367aSGreg Roach    private const TEMPLATE_PATH = 'resources/views/';
38dd6b2bfcSGreg Roach
39dd6b2bfcSGreg Roach    // File extension for our template files.
4016d6367aSGreg Roach    private const TEMPLATE_EXTENSION = '.phtml';
41dd6b2bfcSGreg Roach
42f3281ad6SGreg Roach    /**
43f3281ad6SGreg Roach     * @var string The (file) name of the view.
44f3281ad6SGreg Roach     */
45f3281ad6SGreg Roach    private $name;
46f3281ad6SGreg Roach
47f3281ad6SGreg Roach    /**
48f3281ad6SGreg Roach     * @var mixed[] Data to be inserted into the view.
49f3281ad6SGreg Roach     */
50f3281ad6SGreg Roach    private $data;
51f3281ad6SGreg Roach
52f3281ad6SGreg Roach    /**
5308d24c7aSGreg Roach     * @var mixed[] Data to be inserted into all views.
5408d24c7aSGreg Roach     */
5508d24c7aSGreg Roach    private static $shared_data = [];
5608d24c7aSGreg Roach
5708d24c7aSGreg Roach    /**
58ecf66805SGreg Roach     * @var string Implementation of Blade "stacks".
59ecf66805SGreg Roach     */
60ecf66805SGreg Roach    private static $stack;
61ecf66805SGreg Roach
62ecf66805SGreg Roach    /**
63ecf66805SGreg Roach     * @var array[] Implementation of Blade "stacks".
64ecf66805SGreg Roach     */
65ecf66805SGreg Roach    private static $stacks = [];
66ecf66805SGreg Roach
67ecf66805SGreg Roach    /**
68f3281ad6SGreg Roach     * Createa view from a template name and optional data.
69f3281ad6SGreg Roach     *
70fa4036e8SGreg Roach     * @param string $name
71f3281ad6SGreg Roach     * @param array  $data
72f3281ad6SGreg Roach     */
73fa4036e8SGreg Roach    public function __construct(string $name, $data = [])
74c1010edaSGreg Roach    {
75f3281ad6SGreg Roach        $this->name = $name;
76f3281ad6SGreg Roach        $this->data = $data;
77f3281ad6SGreg Roach    }
78f3281ad6SGreg Roach
79f3281ad6SGreg Roach    /**
8008d24c7aSGreg Roach     * Shared data that is available to all views.
81962e29c9SGreg Roach     *
82962e29c9SGreg Roach     * @param string $key
83962e29c9SGreg Roach     * @param mixed  $value
84fa4036e8SGreg Roach     *
85fa4036e8SGreg Roach     * @return void
8608d24c7aSGreg Roach     */
87c1010edaSGreg Roach    public static function share(string $key, $value)
88c1010edaSGreg Roach    {
8908d24c7aSGreg Roach        self::$shared_data[$key] = $value;
9008d24c7aSGreg Roach    }
9108d24c7aSGreg Roach
9208d24c7aSGreg Roach    /**
93ecf66805SGreg Roach     * Implementation of Blade "stacks".
94ecf66805SGreg Roach     *
95ecf66805SGreg Roach     * @see https://laravel.com/docs/5.5/blade#stacks
96962e29c9SGreg Roach     *
97962e29c9SGreg Roach     * @param string $stack
98fa4036e8SGreg Roach     *
99fa4036e8SGreg Roach     * @return void
100ecf66805SGreg Roach     */
101c1010edaSGreg Roach    public static function push(string $stack)
102c1010edaSGreg Roach    {
103ecf66805SGreg Roach        self::$stack = $stack;
104*d7952a34SGreg Roach
105ecf66805SGreg Roach        ob_start();
106ecf66805SGreg Roach    }
107ecf66805SGreg Roach
108ecf66805SGreg Roach    /**
109ecf66805SGreg Roach     * Implementation of Blade "stacks".
110fa4036e8SGreg Roach     *
111fa4036e8SGreg Roach     * @return void
112ecf66805SGreg Roach     */
113c1010edaSGreg Roach    public static function endpush()
114c1010edaSGreg Roach    {
11588de55fdSRico Sonntag        $content = ob_get_clean();
11688de55fdSRico Sonntag
117*d7952a34SGreg Roach        self::$stacks[self::$stack][] = $content;
118*d7952a34SGreg Roach    }
119*d7952a34SGreg Roach
120*d7952a34SGreg Roach    /**
121*d7952a34SGreg Roach     * Variant of push that will only add one copy of each item.
122*d7952a34SGreg Roach     *
123*d7952a34SGreg Roach     * @param string $stack
124*d7952a34SGreg Roach     *
125*d7952a34SGreg Roach     * @return void
126*d7952a34SGreg Roach     */
127*d7952a34SGreg Roach    public static function pushunique(string $stack)
128*d7952a34SGreg Roach    {
129*d7952a34SGreg Roach        self::$stack = $stack;
130*d7952a34SGreg Roach
131*d7952a34SGreg Roach        ob_start();
132*d7952a34SGreg Roach    }
133*d7952a34SGreg Roach
134*d7952a34SGreg Roach    /**
135*d7952a34SGreg Roach     * Variant of push that will only add one copy of each item.
136*d7952a34SGreg Roach     *
137*d7952a34SGreg Roach     * @return void
138*d7952a34SGreg Roach     */
139*d7952a34SGreg Roach    public static function endpushunique()
140*d7952a34SGreg Roach    {
141*d7952a34SGreg Roach        $content = ob_get_clean();
142*d7952a34SGreg Roach
143*d7952a34SGreg Roach        self::$stacks[self::$stack][sha1($content)] = $content;
144ecf66805SGreg Roach    }
145ecf66805SGreg Roach
146ecf66805SGreg Roach    /**
147ecf66805SGreg Roach     * Implementation of Blade "stacks".
148ecf66805SGreg Roach     *
149962e29c9SGreg Roach     * @param string $stack
150962e29c9SGreg Roach     *
151ecf66805SGreg Roach     * @return string
152ecf66805SGreg Roach     */
153c1010edaSGreg Roach    public static function stack(string $stack): string
154c1010edaSGreg Roach    {
155ecf66805SGreg Roach        $content = implode('', self::$stacks[$stack] ?? []);
156ecf66805SGreg Roach
157ecf66805SGreg Roach        self::$stacks[$stack] = [];
158ecf66805SGreg Roach
159ecf66805SGreg Roach        return $content;
160ecf66805SGreg Roach    }
161ecf66805SGreg Roach
162ecf66805SGreg Roach    /**
163f3281ad6SGreg Roach     * Render a view.
164f3281ad6SGreg Roach     *
165f3281ad6SGreg Roach     * @return string
16670f31542SGreg Roach     * @throws Throwable
167f3281ad6SGreg Roach     */
1688f53f488SRico Sonntag    public function render(): string
169c1010edaSGreg Roach    {
1704a86d714SGreg Roach        $variables_for_view = $this->data + self::$shared_data;
1714a86d714SGreg Roach        extract($variables_for_view);
172f3281ad6SGreg Roach
17370f31542SGreg Roach        try {
174f3281ad6SGreg Roach            ob_start();
175bc241c54SGreg Roach            // Do not use require, so we can catch errors for missing files
176bc241c54SGreg Roach            include $this->getFilenameForView($this->name);
17775d70144SGreg Roach
178f3281ad6SGreg Roach            return ob_get_clean();
17970f31542SGreg Roach        } catch (Throwable $ex) {
18070f31542SGreg Roach            ob_end_clean();
18170f31542SGreg Roach            throw $ex;
18270f31542SGreg Roach        }
183f3281ad6SGreg Roach    }
184f3281ad6SGreg Roach
185f3281ad6SGreg Roach    /**
18675d70144SGreg Roach     * Allow a theme to override the default views.
187f3281ad6SGreg Roach     *
188f3281ad6SGreg Roach     * @param string $view_name
189f3281ad6SGreg Roach     *
19075d70144SGreg Roach     * @return string
19150c68a25SGreg Roach     * @throws Exception
192f3281ad6SGreg Roach     */
1938f53f488SRico Sonntag    public function getFilenameForView($view_name): string
194c1010edaSGreg Roach    {
19550c68a25SGreg Roach        foreach ($this->paths() as $path) {
196dd6b2bfcSGreg Roach            $view_file = $path . $view_name . self::TEMPLATE_EXTENSION;
19775d70144SGreg Roach
19850c68a25SGreg Roach            if (is_file($view_file)) {
19950c68a25SGreg Roach                return $view_file;
200f21917b2SGreg Roach            }
201f3281ad6SGreg Roach        }
202f3281ad6SGreg Roach
20350c68a25SGreg Roach        throw new Exception('View not found: ' . e($view_name));
20450c68a25SGreg Roach    }
20550c68a25SGreg Roach
206f3281ad6SGreg Roach    /**
207f3281ad6SGreg Roach     * Cerate and render a view in a single operation.
208f3281ad6SGreg Roach     *
209f3281ad6SGreg Roach     * @param string  $name
210f3281ad6SGreg Roach     * @param mixed[] $data
211f3281ad6SGreg Roach     *
212f3281ad6SGreg Roach     * @return string
213f3281ad6SGreg Roach     */
2148f53f488SRico Sonntag    public static function make($name, $data = []): string
215c1010edaSGreg Roach    {
216f3281ad6SGreg Roach        $view = new static($name, $data);
217f3281ad6SGreg Roach
21844116e73SGreg Roach        DebugBar::addView($name, $data);
21944116e73SGreg Roach
220f3281ad6SGreg Roach        return $view->render();
221f3281ad6SGreg Roach    }
22250c68a25SGreg Roach
22350c68a25SGreg Roach    /**
22450c68a25SGreg Roach     * @return string[]
22550c68a25SGreg Roach     */
22650c68a25SGreg Roach    private function paths(): array
22750c68a25SGreg Roach    {
22850c68a25SGreg Roach        static $paths = [];
22950c68a25SGreg Roach
23050c68a25SGreg Roach        if (empty($paths)) {
23150c68a25SGreg Roach            // Module views
23250c68a25SGreg Roach            // @TODO - this includes disabled modules.
233af9cb431SGreg Roach            //$paths = glob(WT_ROOT . Webtrees::MODULES_PATH . '*/' . self::TEMPLATE_PATH);
23450c68a25SGreg Roach            // Theme views
235af9cb431SGreg Roach            // @TODO - this won't work during setup.
236cab242e7SGreg Roach            //$paths[] = WT_ROOT . Webtrees::THEMES_PATH . app(ModuleThemeInterface::class)->name() . '/' . self::TEMPLATE_PATH;
23750c68a25SGreg Roach            // Core views
238dd6b2bfcSGreg Roach            $paths[] = WT_ROOT . self::TEMPLATE_PATH;
23950c68a25SGreg Roach
240bdb3725aSGreg Roach            $paths = array_filter($paths, function (string $path): bool {
241bdb3725aSGreg Roach                return is_dir($path);
242bdb3725aSGreg Roach            });
24350c68a25SGreg Roach        }
24450c68a25SGreg Roach
24550c68a25SGreg Roach        return $paths;
24650c68a25SGreg Roach    }
247f3281ad6SGreg Roach}
248