1 <?php
2 /**
3 * @package Joomla.Libraries
4 * @subpackage Helper
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE.txt
8 */
9
10 defined('_JEXEC') or die;
11
12 jimport('joomla.filesystem.file');
13 jimport('joomla.filesystem.folder');
14
15 /**
16 * Base install script for use by extensions providing helper methods for common behaviours.
17 *
18 * @since 3.6
19 */
20 class JInstallerScript
21 {
22 /**
23 * The version number of the extension.
24 *
25 * @var string
26 * @since 3.6
27 */
28 protected $release;
29
30 /**
31 * The table the parameters are stored in.
32 *
33 * @var string
34 * @since 3.6
35 */
36 protected $paramTable;
37
38 /**
39 * The extension name. This should be set in the installer script.
40 *
41 * @var string
42 * @since 3.6
43 */
44 protected $extension;
45
46 /**
47 * A list of files to be deleted
48 *
49 * @var array
50 * @since 3.6
51 */
52 protected $deleteFiles = array();
53
54 /**
55 * A list of folders to be deleted
56 *
57 * @var array
58 * @since 3.6
59 */
60 protected $deleteFolders = array();
61
62 /**
63 * A list of CLI script files to be copied to the cli directory
64 *
65 * @var array
66 * @since 3.6
67 */
68 protected $cliScriptFiles = array();
69
70 /**
71 * Minimum PHP version required to install the extension
72 *
73 * @var string
74 * @since 3.6
75 */
76 protected $minimumPhp;
77
78 /**
79 * Minimum Joomla! version required to install the extension
80 *
81 * @var string
82 * @since 3.6
83 */
84 protected $minimumJoomla;
85
86 /**
87 * Allow downgrades of your extension
88 *
89 * Use at your own risk as if there is a change in functionality people may wish to downgrade.
90 *
91 * @var boolean
92 * @since 3.6
93 */
94 protected $allowDowngrades = false;
95
96 /**
97 * Function called before extension installation/update/removal procedure commences
98 *
99 * @param string $type The type of change (install, update or discover_install, not uninstall)
100 * @param JInstallerAdapter $parent The class calling this method
101 *
102 * @return boolean True on success
103 *
104 * @since 3.6
105 */
106 public function preflight($type, $parent)
107 {
108 // Check for the minimum PHP version before continuing
109 if (!empty($this->minimumPhp) && version_compare(PHP_VERSION, $this->minimumPhp, '<'))
110 {
111 JLog::add(JText::sprintf('JLIB_INSTALLER_MINIMUM_PHP', $this->minimumPhp), JLog::WARNING, 'jerror');
112
113 return false;
114 }
115
116 // Check for the minimum Joomla version before continuing
117 if (!empty($this->minimumJoomla) && version_compare(JVERSION, $this->minimumJoomla, '<'))
118 {
119 JLog::add(JText::sprintf('JLIB_INSTALLER_MINIMUM_JOOMLA', $this->minimumJoomla), JLog::WARNING, 'jerror');
120
121 return false;
122 }
123
124 // Extension manifest file version
125 $this->release = $parent->get('manifest')->version;
126 $extensionType = substr($this->extension, 0, 3);
127
128 // Modules parameters are located in the module table - else in the extension table
129 if ($extensionType === 'mod')
130 {
131 $this->paramTable = '#__modules';
132 }
133 else
134 {
135 $this->paramTable = '#__extensions';
136 }
137
138 // Abort if the extension being installed is not newer than the currently installed version
139 if (!$this->allowDowngrades && strtolower($type) === 'update')
140 {
141 $manifest = $this->getItemArray('manifest_cache', '#__extensions', 'element', JFactory::getDbo()->quote($this->extension));
142 $oldRelease = $manifest['version'];
143
144 if (version_compare($this->release, $oldRelease, '<'))
145 {
146 JFactory::getApplication()->enqueueMessage(JText::sprintf('JLIB_INSTALLER_INCORRECT_SEQUENCE', $oldRelease, $this->release), 'error');
147
148 return false;
149 }
150 }
151
152 return true;
153 }
154
155 /**
156 * Gets each instance of a module in the #__modules table
157 *
158 * @param boolean $isModule True if the extension is a module as this can have multiple instances
159 *
160 * @return array An array of ID's of the extension
161 *
162 * @since 3.6
163 */
164 public function getInstances($isModule)
165 {
166 $db = JFactory::getDbo();
167 $query = $db->getQuery(true);
168
169 // Select the item(s) and retrieve the id
170 $query->select($db->quoteName('id'));
171
172 if ($isModule)
173 {
174 $query->from($db->quoteName('#__modules'))
175 ->where($db->quoteName('module') . ' = ' . $db->quote($this->extension));
176 }
177 else
178 {
179 $query->from($db->quoteName('#__extensions'))
180 ->where($db->quoteName('element') . ' = ' . $db->quote($this->extension));
181 }
182
183 // Set the query and obtain an array of id's
184 return $db->setQuery($query)->loadColumn();
185 }
186
187 /**
188 * Gets parameter value in the extensions row of the extension table
189 *
190 * @param string $name The name of the parameter to be retrieved
191 * @param integer $id The id of the item in the Param Table
192 *
193 * @return string The parameter desired
194 *
195 * @since 3.6
196 */
197 public function getParam($name, $id = 0)
198 {
199 if (!is_int($id) || $id == 0)
200 {
201 // Return false if there is no item given
202 return false;
203 }
204
205 $params = $this->getItemArray('params', $this->paramTable, 'id', $id);
206
207 return $params[$name];
208 }
209
210 /**
211 * Sets parameter values in the extensions row of the extension table. Note that the
212 * this must be called separately for deleting and editing. Note if edit is called as a
213 * type then if the param doesn't exist it will be created
214 *
215 * @param array $param_array The array of parameters to be added/edited/removed
216 * @param string $type The type of change to be made to the param (edit/remove)
217 * @param integer $id The id of the item in the relevant table
218 *
219 * @return boolean True on success
220 *
221 * @since 3.6
222 */
223 public function setParams($param_array = null, $type = 'edit', $id = 0)
224 {
225 if (!is_int($id) || $id == 0)
226 {
227 // Return false if there is no valid item given
228 return false;
229 }
230
231 $params = $this->getItemArray('params', $this->paramTable, 'id', $id);
232
233 if ($param_array)
234 {
235 foreach ($param_array as $name => $value)
236 {
237 if ($type === 'edit')
238 {
239 // Add or edit the new variable(s) to the existing params
240 if (is_array($value))
241 {
242 // Convert an array into a json encoded string
243 $params[(string) $name] = array_values($value);
244 }
245 else
246 {
247 $params[(string) $name] = (string) $value;
248 }
249 }
250 elseif ($type === 'remove')
251 {
252 // Unset the parameter from the array
253 unset($params[(string) $name]);
254 }
255 }
256 }
257
258 // Store the combined new and existing values back as a JSON string
259 $paramsString = json_encode($params);
260
261 $db = JFactory::getDbo();
262 $query = $db->getQuery(true)
263 ->update($db->quoteName($this->paramTable))
264 ->set('params = ' . $db->quote($paramsString))
265 ->where('id = ' . $id);
266
267 // Update table
268 $db->setQuery($query)->execute();
269
270 return true;
271 }
272
273 /**
274 * Builds a standard select query to produce better DRY code in this script.
275 * This should produce a single unique cell which is json encoded - it will then
276 * return an associated array with this data in.
277 *
278 * @param string $element The element to get from the query
279 * @param string $table The table to search for the data in
280 * @param string $column The column of the database to search from
281 * @param mixed $identifier The integer id or the already quoted string
282 *
283 * @return array Associated array containing data from the cell
284 *
285 * @since 3.6
286 */
287 public function getItemArray($element, $table, $column, $identifier)
288 {
289 // Get the DB and query objects
290 $db = JFactory::getDbo();
291
292 // Build the query
293 $query = $db->getQuery(true)
294 ->select($db->quoteName($element))
295 ->from($db->quoteName($table))
296 ->where($db->quoteName($column) . ' = ' . $identifier);
297 $db->setQuery($query);
298
299 // Load the single cell and json_decode data
300 return json_decode($db->loadResult(), true);
301 }
302
303 /**
304 * Remove the files and folders in the given array from
305 *
306 * @return void
307 *
308 * @since 3.6
309 */
310 public function removeFiles()
311 {
312 if (!empty($this->deleteFiles))
313 {
314 foreach ($this->deleteFiles as $file)
315 {
316 if (file_exists(JPATH_ROOT . $file) && !JFile::delete(JPATH_ROOT . $file))
317 {
318 echo JText::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $file) . '<br />';
319 }
320 }
321 }
322
323 if (!empty($this->deleteFolders))
324 {
325 foreach ($this->deleteFolders as $folder)
326 {
327 if (JFolder::exists(JPATH_ROOT . $folder) && !JFolder::delete(JPATH_ROOT . $folder))
328 {
329 echo JText::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $folder) . '<br />';
330 }
331 }
332 }
333 }
334
335 /**
336 * Moves the CLI scripts into the CLI folder in the CMS
337 *
338 * @return void
339 *
340 * @since 3.6
341 */
342 public function moveCliFiles()
343 {
344 if (!empty($this->cliScriptFiles))
345 {
346 foreach ($this->cliScriptFiles as $file)
347 {
348 $name = basename($file);
349
350 if (file_exists(JPATH_ROOT . $file) && !JFile::move(JPATH_ROOT . $file, JPATH_ROOT . '/cli/' . $name))
351 {
352 echo JText::sprintf('JLIB_INSTALLER_FILE_ERROR_MOVE', $name);
353 }
354 }
355 }
356 }
357 }
358