-
Notifications
You must be signed in to change notification settings - Fork 3
/
VestaModuleTrait.php
391 lines (323 loc) · 11.8 KB
/
VestaModuleTrait.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
<?php
namespace Vesta;
use Fisharebest\Webtrees\Http\Exceptions\HttpAccessDeniedException;
use Fisharebest\Webtrees\Http\Exceptions\HttpNotFoundException;
use Fisharebest\Webtrees\Schema\MigrationInterface;
use Fisharebest\Webtrees\View;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Support\Str;
use PDOException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Vesta\ControlPanelUtils\ControlPanelUtils;
use Vesta\ControlPanelUtils\Model\ControlPanelPreferences;
use function GuzzleHttp\json_decode;
use function redirect;
use function response;
use function route;
use function view;
trait VestaModuleTrait {
use VestaModuleCustomTrait;
protected function getVestaSymbol() {
return json_decode('"\u26B6"');
}
protected abstract function getMainTitle();
public function getTabTitle($mainTabTitle) {
$prefix = '';
$vesta_show = boolval($this->getPreference('VESTA_TAB', '1'));
if ($vesta_show) {
$prefix = $this->getVestaSymbol() . ' ';
}
return $prefix . $mainTabTitle;
}
public function getChartTitle($mainChartTitle) {
$prefix = '';
$vesta_show = boolval($this->getPreference('VESTA_CHART', '1'));
if ($vesta_show) {
$prefix = $this->getVestaSymbol() . ' ';
}
return $prefix . $mainChartTitle;
}
public function getListTitle($mainListTitle) {
$prefix = '';
$vesta_show = boolval($this->getPreference('VESTA_LIST', '1'));
if ($vesta_show) {
$prefix = $this->getVestaSymbol() . ' ';
}
return $prefix . $mainListTitle;
}
public function getMenuTitle($mainMenuTitle) {
$prefix = '';
$vesta_show = boolval($this->getPreference('VESTA_MENU', '1'));
if ($vesta_show) {
$prefix = $this->getVestaSymbol() . ' ';
}
return $prefix . $mainMenuTitle;
}
public function getSidebarTitle($mainTabTitle) {
$prefix = '';
$vesta_show = boolval($this->getPreference('VESTA_SIDEBAR', '1'));
if ($vesta_show) {
$prefix = $this->getVestaSymbol() . ' ';
}
return $prefix . $mainTabTitle;
}
public function getBlockTitle($mainBlockTitle) {
$prefix = '';
$vesta_show = boolval($this->getPreference('VESTA_BLOCK', '1'));
if ($vesta_show) {
$prefix = $this->getVestaSymbol() . ' ';
}
return $prefix . $mainBlockTitle;
}
public function title(): string {
$prefix = '';
$vesta_show = true/* boolval($this->getPreference('VESTA', '1')) */;
if ($vesta_show) {
$prefix = $this->getVestaSymbol() . ' ';
}
$title = $this->getMainTitle();
//no longer required, main title expected to be translated in Vesta Common
/*
if (!$this->isEnabled()) {
$title = ModuleI18N::translate($this, $title);
}
*/
return $prefix . $title;
}
public function description(): string {
$description = $this->getShortDescription();
if (!$this->isEnabled()) {
$description = ModuleI18N::translate($this, $description);
}
return $description;
}
public function getConfigLink(): string {
return route('module', [
'module' => $this->name(),
'action' => 'Admin',
]);
}
public function getAdminAction(ServerRequestInterface $request): ResponseInterface {
//fancy way, example from StoriesModule.php
/*
$this->layout = 'layouts/administration';
return $this->viewResponse('modules/stories/config', [
'stories' => $stories,
'title' => $this->title() . ' — ' . $tree->title(),
'tree' => $tree,
'tree_names' => Tree::getNameList(),
]);
*/
//plain way
return response($this->editConfig());
}
public function postAdminAction(ServerRequestInterface $request): ResponseInterface {
$this->saveConfig($request);
$url = route('module', [
'module' => $this->name(),
'action' => 'Admin',
]);
return redirect($url);
}
/**
* @return ControlPanelPreferences
*/
protected abstract function createPrefs();
/**
* @return string
*/
protected abstract function getShortDescription();
/**
* @return string[]
*/
protected abstract function getFullDescription();
/**
* return html (form to edit configuration settings)
*/
protected function editConfig() {
ob_start();
$this->editConfigAfterFaq();
$editConfigAfterFaq = ob_get_clean();
ob_start();
$this->editConfigBeforeFaq();
$editConfigBeforeFaq = ob_get_clean();
ob_start();
$utils = new ControlPanelUtils($this);
$utils->printPrefs($this->createPrefs(), $this->name());
$prefs = ob_get_clean();
$innerHtml = view(VestaUtils::vestaViewsNamespace() . '::vesta_config', [
'title' => $this->title(),
'vesta' => $this->getVestaSymbol(),
'fullDescription' => $this->getFullDescription(),
'editConfigBeforeFaq' => $editConfigBeforeFaq,
'editConfigAfterFaq' => $editConfigAfterFaq,
'prefs' => $prefs
]);
// Insert the view into the (main) layout
$html = View::make('layouts/administration', [
'title' => $this->title(),
'content' => $innerHtml
]);
return $html;
}
/**
* OverrideHook
*
* echoes html
*/
protected function editConfigBeforeFaq() {
}
/**
* OverrideHook
*
* echoes html
*/
protected function editConfigAfterFaq() {
}
/**
* Save updated configuration settings.
*/
protected function saveConfig(ServerRequestInterface $request) {
$utils = new ControlPanelUtils($this);
$utils->savePostData($request, $this->createPrefs());
}
//same as Database::getSchema, but use module settings instead of site settings (Issue #3 in personal_facts_with_hooks)
protected function updateSchema($namespace, $schema_name, $target_version): bool {
try {
$current_version = intval($this->getPreference($schema_name));
} catch (PDOException $ex) {
// During initial installation, the site_preference table won’t exist.
$current_version = 0;
}
$updates_applied = false;
// Update the schema, one version at a time.
while ($current_version < $target_version) {
$class = $namespace . '\\Migration' . $current_version;
/** @var MigrationInterface $migration */
$migration = new $class();
$migration->upgrade();
$current_version++;
//when a module is first installed, we may not be able to setPreference at this point
////(if this is called e.g. from SetName())
//because of foreign key constraints:
//the module may not have been inserted in the 'module' table at this point!
//cf. ModuleService.all()
//
//not that critical, we can just set the preference next time
//
//let's just check this directly (using ModuleService at this point may lead to looping, if we're indirectly called from there)
if (DB::table('module')->where('module_name', '=', $this->name())->exists()) {
$this->setPreference($schema_name, (string) $current_version);
}
$updates_applied = true;
}
return $updates_applied;
}
////////////////////////////////////////////////////////////////////////////////
public function boot(): void {
// Register a namespace for our views.
View::registerNamespace($this->name(), $this->resourcesFolder() . 'views/');
// and for Vesta views.
View::registerNamespace(VestaUtils::vestaViewsNamespace(), __DIR__ . '/resources/views/');
$this->onBoot();
}
/**
* OverrideHook
*/
protected function onBoot(): void {
}
////////////////////////////////////////////////////////////////////////////////
/**
* OverrideHook
*/
public function assetsViaViews(): array {
return [];
}
//adapted from ModuleCustomTrait
/**
* Create a URL for an asset.
*
* @param string $asset e.g. "css/theme.css" or "img/banner.png"
*
* @return string
*/
public function assetUrl(string $asset): string {
$assetFile = $asset;
$assetsViaViews = $this->assetsViaViews();
if (array_key_exists($asset, $assetsViaViews)) {
$assetFile = 'views/' . $assetsViaViews[$asset] . '.phtml';
}
$file = $this->resourcesFolder() . $assetFile;
// Add the file's modification time to the URL, so we can set long expiry cache headers.
//[RC] assume this is also ok for views (i.e. assume the rendered content isn't dynamic)
// otherwise override assetAdditionalHash()!
$hash = hash("md5", filemtime($file) . $this->assetAdditionalHash($asset));
return route('module', [
'module' => $this->name(),
'action' => 'asset',
'asset' => $asset,
'hash' => $hash,
]);
}
//OverrideHook
public function assetAdditionalHash(string $asset): string {
return "";
}
//adapted from ModuleCustomTrait
/**
* Serve a CSS/JS file.
*
* @param ServerRequestInterface $request
*
* @return ResponseInterface
*/
public function getAssetAction(ServerRequestInterface $request): ResponseInterface {
// The file being requested. e.g. "css/theme.css"
$params = $request->getQueryParams();
if (!array_key_exists('asset', $params)) {
throw new HttpNotFoundException('No asset specified.');
}
$asset = $params['asset'];
// Do not allow requests that try to access parent folders.
if (Str::contains($asset, '..')) {
throw new HttpAccessDeniedException($asset);
}
$assetsViaViews = $this->assetsViaViews();
if (array_key_exists($asset, $assetsViaViews)) {
$assetFile = $assetsViaViews[$asset];
$assertRouter = function (string $asset) {
return $this->assetUrl($asset);
};
$content = view($this->name() . '::' . $assetFile, [
'assetRouter' => $assertRouter,
'module' => $this
]);
} else {
$file = $this->resourcesFolder() . $asset;
if (!file_exists($file)) {
throw new HttpNotFoundException('Asset \'' . $asset . '\' not found.');
}
$content = file_get_contents($file);
}
$extension = pathinfo($asset, PATHINFO_EXTENSION);
$mime_types = [
'css' => 'text/css',
'gif' => 'image/gif',
'js' => 'application/javascript',
'jpg' => 'image/jpg',
'jpeg' => 'image/jpg',
'json' => 'application/json',
'png' => 'image/png',
'txt' => 'text/plain',
];
$mime_type = $mime_types[$extension] ?? 'application/octet-stream';
//$expiry_date = Registry::timestampFactory()->now()->addYears(10)->toDateTimeString();
$headers = [
'content-type' => $mime_type,
//'Expires' => $expiry_date, //apparently outdated, see also EmitResponse.php, i.e. we have to use Cache-Control!
'Cache-Control' => 'public,max-age=31536000', //what webtrees uses elsewhere: ~10 years
];
return response($content, 200, $headers);
}
}