Repository navigation
Expand file tree
/
Copy pathscript.php
More file actions
307 lines (264 loc) · 10.4 KB
/
Copy pathscript.php
File metadata and controls
307 lines (264 loc) · 10.4 KB
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
<?php
/**
* @package TLWeb.Module
* @subpackage mod_prettyreviews
*
* @copyright Copyright (C) 2024 TLWebdesign. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Installer\InstallerAdapter;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\ParameterType;
/**
* Script file of Prettyreviews module
*/
class mod_prettyreviewsInstallerScript
{
/**
* @var string
*/
protected string $minimumJoomla = '4.0';
/**
* @var string
*/
protected string $minimumPhp;
/**
* The version being upgraded from, captured during an update preflight.
*
* @var ?string
*/
protected ?string $fromVersion = null;
/**
* Extension script constructor.
*
* @return void
*/
public function __construct()
{
$this->minimumPhp = JOOMLA_MINIMUM_PHP;
}
/**
* Method to install the extension
*
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*/
public function install(InstallerAdapter $parent): bool
{
echo Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_INSTALL');
return true;
}
/**
* Method to uninstall the extension
*
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*/
public function uninstall(InstallerAdapter $parent): bool
{
echo Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_UNINSTALL');
return true;
}
/**
* Method to update the extension
*
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*/
public function update(InstallerAdapter $parent): bool
{
echo Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_UPDATE');
return true;
}
/**
* Function called before extension installation/update/removal procedure commences
*
* @param string $type The type of change (install, update or discover_install, not uninstall)
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*/
public function preflight(string $type, InstallerAdapter $parent): bool
{
// Check for the minimum PHP version before continuing
if (!empty($this->minimumPhp) && version_compare(PHP_VERSION, $this->minimumPhp, '<')) {
Log::add(Text::sprintf('JLIB_INSTALLER_MINIMUM_PHP', $this->minimumPhp), Log::WARNING, 'jerror');
return false;
}
// Check for the minimum Joomla version before continuing
if (!empty($this->minimumJoomla) && version_compare(JVERSION, $this->minimumJoomla, '<')) {
Log::add(Text::sprintf('JLIB_INSTALLER_MINIMUM_JOOMLA', $this->minimumJoomla), Log::WARNING, 'jerror');
return false;
}
// Capture the version we are upgrading from. At preflight the extensions
// table still holds the OLD manifest, so this is the pre-update version.
if ($type === 'update') {
$this->fromVersion = $this->getInstalledVersion();
}
echo Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_PREFLIGHT');
return true;
}
/**
* Function called after extension installation/update/removal procedure commences
*
* @param string $type The type of change (install, update or discover_install, not uninstall)
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*/
public function postflight(string $type, InstallerAdapter $parent): bool
{
if ($type == "update") {
echo Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_RESAVE_MODULE');
// Upgrading from a pre-2.0.0 single-carousel release: pin every existing
// module instance to one column on all breakpoints so its appearance does
// not change. New 2.0+ modules store their own column settings and are
// therefore left untouched.
if ($this->fromVersion !== null && version_compare($this->fromVersion, '2.0.0', '<')) {
// Announced only once it has happened. This is the one part of
// postflight that writes to the database, so whether it ran is not
// something to claim before knowing.
if ($this->lockLegacyColumnsToSingle()) {
echo Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_COLUMNS_PRESERVED');
}
}
// From 2.2.0 reviewer photos are stored on this site instead of being
// loaded from Google by the visitor's browser. A cache written by an
// earlier release names no stored photo, so until it is refreshed its
// reviewers show an initials avatar. Nothing is downloaded while a page
// is viewed, so only an explicit refresh can fill them in -- which the
// administrator has to be told to do.
if (
$this->fromVersion !== null
&& version_compare($this->fromVersion, '2.2.0', '<')
&& $this->hasCachedReviews()
) {
// As a system message rather than installer output: this one asks the
// administrator to go and do something afterwards, so it wants the
// alert, and printing it as well only says the same thing twice.
Factory::getApplication()->enqueueMessage(
strip_tags(Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_REFRESH_PHOTOS')),
'warning'
);
}
}
echo Text::_('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_POSTFLIGHT');
return true;
}
/**
* Whether any module has reviews cached, and so photos worth refreshing.
*
* A site with nothing cached displays no reviews at all, so there is nothing for
* an administrator to act on and the notice would only be noise.
*
* @return boolean
*/
private function hasCachedReviews(): bool
{
$files = glob(JPATH_ROOT . '/media/mod_prettyreviews/data-*.json');
return \is_array($files) && $files !== [];
}
/**
* Reads the currently-installed version from the extensions table. During an
* update preflight this still reflects the version being replaced.
*
* @return ?string
*/
private function getInstalledVersion(): ?string
{
try {
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true)
->select($db->quoteName('manifest_cache'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('module'))
->where($db->quoteName('element') . ' = ' . $db->quote('mod_prettyreviews'));
$db->setQuery($query);
$cache = $db->loadResult();
} catch (\Throwable $e) {
return null;
}
if (!$cache) {
return null;
}
$decoded = json_decode((string) $cache, true);
return \is_array($decoded) && isset($decoded['version']) ? (string) $decoded['version'] : null;
}
/**
* Pins every existing module instance to a single column on all breakpoints,
* preserving pre-2.0.0 behaviour. Only adds the keys when they are absent, so an
* administrator's saved choices are never overwritten -- which also makes the
* migration safe to run again after a partial one.
*
* A database failure here must not take the update down with it. By postflight the
* new files are already in place, and an exception leaving it reaches the installer
* adapter, which aborts and unwinds an update that had otherwise finished. So the
* administrator is told instead: modules left unmigrated fall back to the field
* defaults and widen to four columns on desktop, which the settings can put right.
*
* @return boolean True when the migration completed.
*/
private function lockLegacyColumnsToSingle(): bool
{
try {
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true)
->select([$db->quoteName('id'), $db->quoteName('params')])
->from($db->quoteName('#__modules'))
->where($db->quoteName('module') . ' = ' . $db->quote('mod_prettyreviews'));
$db->setQuery($query);
$modules = $db->loadObjectList();
if (empty($modules)) {
return true;
}
$keys = [
'carousel_columns_mobile',
'carousel_columns_tablet',
'carousel_columns_desktop',
'carousel_columns_wide',
];
foreach ($modules as $module) {
$params = json_decode((string) $module->params, true);
if (!\is_array($params)) {
$params = [];
}
$changed = false;
foreach ($keys as $key) {
if (!\array_key_exists($key, $params)) {
$params[$key] = "1";
$changed = true;
}
}
if (!$changed) {
continue;
}
$encoded = json_encode($params);
$id = (int) $module->id;
$update = $db->getQuery(true)
->update($db->quoteName('#__modules'))
->set($db->quoteName('params') . ' = :params')
->where($db->quoteName('id') . ' = :id')
->bind(':params', $encoded)
->bind(':id', $id, ParameterType::INTEGER);
$db->setQuery($update);
$db->execute();
}
return true;
} catch (\Throwable $e) {
Log::add(
Text::sprintf('MOD_PRETTYREVIEWS_INSTALLERSCRIPT_COLUMNS_FAILED', $e->getMessage()),
Log::WARNING,
'jerror'
);
return false;
}
}
}