xref: /webtrees/app/Module/ModuleCustomTrait.php (revision 02086832f6c85aab8102c1a336d227314e040441)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees\Module;
19
20use Fisharebest\Webtrees\Carbon;
21use Illuminate\Support\Str;
22use Symfony\Component\HttpFoundation\Request;
23use Symfony\Component\HttpFoundation\Response;
24use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
25use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
26
27/**
28 * Trait ModuleCustomTrait - default implementation of ModuleCustomInterface
29 */
30trait ModuleCustomTrait
31{
32    /**
33     * Where does this module store its resources
34     *
35     * @return string
36     */
37    abstract public function resourcesFolder(): string;
38
39    /**
40     * A unique internal name for this module (based on the installation folder).
41     *
42     * @return string
43     */
44    abstract public function name(): string;
45
46    /**
47     * The person or organisation who created this module.
48     *
49     * @return string
50     */
51    public function customModuleAuthorName(): string
52    {
53        return '';
54    }
55
56    /**
57     * The version of this module.
58     *
59     * @return string  e.g. '1.2.3'
60     */
61    public function customModuleVersion(): string
62    {
63        return '';
64    }
65
66    /**
67     * A URL that will provide the latest version of this module.
68     *
69     * @return string
70     */
71    public function customModuleLatestVersionUrl(): string
72    {
73        return '';
74    }
75
76    /**
77     * Where to get support for this module.  Perhaps a github respository?
78     *
79     * @return string
80     */
81    public function customModuleSupportUrl(): string
82    {
83        return '';
84    }
85
86    /**
87     * Additional/updated translations.
88     *
89     * @param string $language
90     *
91     * @return string[]
92     */
93    public function customTranslations(string $language): array
94    {
95        return [];
96    }
97
98    /**
99     * Create a URL for an asset.
100     *
101     * @param string $asset e.g. "css/theme.css" or "img/banner.png"
102     *
103     * @return string
104     */
105    public function assetUrl(string $asset): string
106    {
107        $file = $this->resourcesFolder() . $asset;
108
109        // Add the file's modification time to the URL, so we can set long expiry cache headers.
110        $hash = filemtime($file);
111
112        return route('module', [
113            'module' => $this->name(),
114            'action' => 'asset',
115            'asset'  => $asset,
116            'hash'   => $hash,
117        ]);
118    }
119
120    /**
121     * Serve a CSS/JS file.
122     *
123     * @param Request $request
124     *
125     * @return Response
126     */
127    public function getAssetAction(Request $request): Response
128    {
129        // The file being requested.  e.g. "css/theme.css"
130        $asset = $request->get('asset');
131
132        // Do not allow requests that try to access parent folders.
133        if (Str::contains($asset, '..')) {
134            throw new AccessDeniedHttpException($asset);
135        }
136
137        // Find the file for this asset.
138        // Note that we could also generate CSS files using views/templates.
139        // e.g. $file = view(....
140        $file = $this->resourcesFolder() . $asset;
141
142        if (!file_exists($file)) {
143            throw new NotFoundHttpException($file);
144        }
145
146        $content     = file_get_contents($file);
147        $expiry_date = Carbon::now()->addYears(10);
148
149        $extension = pathinfo($asset, PATHINFO_EXTENSION);
150
151        $mime_types = [
152            'css'  => 'text/css',
153            'gif'  => 'image/gif',
154            'js'   => 'application/javascript',
155            'jpg'  => 'image/jpg',
156            'jpeg' => 'image/jpg',
157            'json' => 'application/json',
158            'png'  => 'image/png',
159            'txt'  => 'text/plain',
160        ];
161
162        $mime_type = $mime_types[$extension] ?? 'application/octet-stream';
163
164        $headers = [
165            'Content-Type' => $mime_type,
166        ];
167
168        $response = new Response($content, Response::HTTP_OK, $headers);
169
170        return $response
171            ->setExpires($expiry_date);
172    }
173}
174