1<?php
2/**
3 * @package     Joomla.Administrator
4 * @subpackage  com_installer
5 *
6 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
7 * @license     GNU General Public License version 2 or later; see LICENSE.txt
8 */
9
10defined('_JEXEC') or die;
11
12JLoader::register('InstallerModel', __DIR__ . '/extension.php');
13
14/**
15 * Installer Manage Model
16 *
17 * @since  1.5
18 */
19class InstallerModelManage extends InstallerModel
20{
21	/**
22	 * Constructor.
23	 *
24	 * @param   array  $config  An optional associative array of configuration settings.
25	 *
26	 * @see     JController
27	 * @since   1.6
28	 */
29	public function __construct($config = array())
30	{
31		if (empty($config['filter_fields']))
32		{
33			$config['filter_fields'] = array(
34				'status',
35				'name',
36				'client_id',
37				'client', 'client_translated',
38				'type', 'type_translated',
39				'folder', 'folder_translated',
40				'package_id',
41				'extension_id',
42			);
43		}
44
45		parent::__construct($config);
46	}
47
48	/**
49	 * Method to auto-populate the model state.
50	 *
51	 * Note. Calling getState in this method will result in recursion.
52	 *
53	 * @param   string  $ordering   An optional ordering field.
54	 * @param   string  $direction  An optional direction (asc|desc).
55	 *
56	 * @return  void
57	 *
58	 * @since   1.6
59	 */
60	protected function populateState($ordering = 'name', $direction = 'asc')
61	{
62		$app = JFactory::getApplication();
63
64		// Load the filter state.
65		$this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string'));
66		$this->setState('filter.client_id', $this->getUserStateFromRequest($this->context . '.filter.client_id', 'filter_client_id', null, 'int'));
67		$this->setState('filter.status', $this->getUserStateFromRequest($this->context . '.filter.status', 'filter_status', '', 'string'));
68		$this->setState('filter.type', $this->getUserStateFromRequest($this->context . '.filter.type', 'filter_type', '', 'string'));
69		$this->setState('filter.folder', $this->getUserStateFromRequest($this->context . '.filter.folder', 'filter_folder', '', 'string'));
70
71		$this->setState('message', $app->getUserState('com_installer.message'));
72		$this->setState('extension_message', $app->getUserState('com_installer.extension_message'));
73		$app->setUserState('com_installer.message', '');
74		$app->setUserState('com_installer.extension_message', '');
75
76		parent::populateState($ordering, $direction);
77	}
78
79	/**
80	 * Enable/Disable an extension.
81	 *
82	 * @param   array  $eid    Extension ids to un/publish
83	 * @param   int    $value  Publish value
84	 *
85	 * @return  boolean  True on success
86	 *
87	 * @since   1.5
88	 */
89	public function publish(&$eid = array(), $value = 1)
90	{
91		$user = JFactory::getUser();
92
93		if (!$user->authorise('core.edit.state', 'com_installer'))
94		{
95			JError::raiseWarning(403, JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));
96
97			return false;
98		}
99
100		$result = true;
101
102		/*
103		 * Ensure eid is an array of extension ids
104		 * TODO: If it isn't an array do we want to set an error and fail?
105		 */
106		if (!is_array($eid))
107		{
108			$eid = array($eid);
109		}
110
111		// Get a table object for the extension type
112		$table = JTable::getInstance('Extension');
113		JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_templates/tables');
114
115		// Enable the extension in the table and store it in the database
116		foreach ($eid as $i => $id)
117		{
118			$table->load($id);
119
120			if ($table->type == 'template')
121			{
122				$style = JTable::getInstance('Style', 'TemplatesTable');
123
124				if ($style->load(array('template' => $table->element, 'client_id' => $table->client_id, 'home' => 1)))
125				{
126					JError::raiseNotice(403, JText::_('COM_INSTALLER_ERROR_DISABLE_DEFAULT_TEMPLATE_NOT_PERMITTED'));
127					unset($eid[$i]);
128					continue;
129				}
130			}
131
132			if ($table->protected == 1)
133			{
134				$result = false;
135				JError::raiseWarning(403, JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));
136			}
137			else
138			{
139				$table->enabled = $value;
140			}
141
142			$context = $this->option . '.' . $this->name;
143			JPluginHelper::importPlugin('extension');
144			JEventDispatcher::getInstance()->trigger('onExtensionChangeState', array($context, $eid, $value));
145
146			if (!$table->store())
147			{
148				$this->setError($table->getError());
149				$result = false;
150			}
151		}
152
153		// Clear the cached extension data and menu cache
154		$this->cleanCache('_system', 0);
155		$this->cleanCache('_system', 1);
156		$this->cleanCache('com_modules', 0);
157		$this->cleanCache('com_modules', 1);
158		$this->cleanCache('mod_menu', 0);
159		$this->cleanCache('mod_menu', 1);
160
161		return $result;
162	}
163
164	/**
165	 * Refreshes the cached manifest information for an extension.
166	 *
167	 * @param   int  $eid  extension identifier (key in #__extensions)
168	 *
169	 * @return  boolean  result of refresh
170	 *
171	 * @since   1.6
172	 */
173	public function refresh($eid)
174	{
175		if (!is_array($eid))
176		{
177			$eid = array($eid => 0);
178		}
179
180		// Get an installer object for the extension type
181		$installer = JInstaller::getInstance();
182		$result = 0;
183
184		// Uninstall the chosen extensions
185		foreach ($eid as $id)
186		{
187			$result |= $installer->refreshManifestCache($id);
188		}
189
190		return $result;
191	}
192
193	/**
194	 * Remove (uninstall) an extension
195	 *
196	 * @param   array  $eid  An array of identifiers
197	 *
198	 * @return  boolean  True on success
199	 *
200	 * @since   1.5
201	 */
202	public function remove($eid = array())
203	{
204		$user = JFactory::getUser();
205
206		if (!$user->authorise('core.delete', 'com_installer'))
207		{
208			JError::raiseWarning(403, JText::_('JERROR_CORE_DELETE_NOT_PERMITTED'));
209
210			return false;
211		}
212
213		/*
214		 * Ensure eid is an array of extension ids in the form id => client_id
215		 * TODO: If it isn't an array do we want to set an error and fail?
216		 */
217		if (!is_array($eid))
218		{
219			$eid = array($eid => 0);
220		}
221
222		// Get an installer object for the extension type
223		$installer = JInstaller::getInstance();
224		$row = JTable::getInstance('extension');
225
226		// Uninstall the chosen extensions
227		$msgs = array();
228		$result = false;
229
230		foreach ($eid as $id)
231		{
232			$id = trim($id);
233			$row->load($id);
234			$result = false;
235
236			$langstring = 'COM_INSTALLER_TYPE_TYPE_' . strtoupper($row->type);
237			$rowtype = JText::_($langstring);
238
239			if (strpos($rowtype, $langstring) !== false)
240			{
241				$rowtype = $row->type;
242			}
243
244			if ($row->type)
245			{
246				$result = $installer->uninstall($row->type, $id);
247
248				// Build an array of extensions that failed to uninstall
249				if ($result === false)
250				{
251					// There was an error in uninstalling the package
252					$msgs[] = JText::sprintf('COM_INSTALLER_UNINSTALL_ERROR', $rowtype);
253
254					continue;
255				}
256
257				// Package uninstalled successfully
258				$msgs[] = JText::sprintf('COM_INSTALLER_UNINSTALL_SUCCESS', $rowtype);
259				$result = true;
260
261				continue;
262			}
263
264			// There was an error in uninstalling the package
265			$msgs[] = JText::sprintf('COM_INSTALLER_UNINSTALL_ERROR', $rowtype);
266		}
267
268		$msg = implode('<br />', $msgs);
269		$app = JFactory::getApplication();
270		$app->enqueueMessage($msg);
271		$this->setState('action', 'remove');
272		$this->setState('name', $installer->get('name'));
273		$app->setUserState('com_installer.message', $installer->message);
274		$app->setUserState('com_installer.extension_message', $installer->get('extension_message'));
275
276		// Clear the cached extension data and menu cache
277		$this->cleanCache('_system', 0);
278		$this->cleanCache('_system', 1);
279		$this->cleanCache('com_modules', 0);
280		$this->cleanCache('com_modules', 1);
281		$this->cleanCache('com_plugins', 0);
282		$this->cleanCache('com_plugins', 1);
283		$this->cleanCache('mod_menu', 0);
284		$this->cleanCache('mod_menu', 1);
285
286		return $result;
287	}
288
289	/**
290	 * Method to get the database query
291	 *
292	 * @return  JDatabaseQuery  The database query
293	 *
294	 * @since   1.6
295	 */
296	protected function getListQuery()
297	{
298		$query = $this->getDbo()->getQuery(true)
299			->select('*')
300			->select('2*protected+(1-protected)*enabled AS status')
301			->from('#__extensions')
302			->where('state = 0');
303
304		// Process select filters.
305		$status   = $this->getState('filter.status');
306		$type     = $this->getState('filter.type');
307		$clientId = $this->getState('filter.client_id');
308		$folder   = $this->getState('filter.folder');
309
310		if ($status != '')
311		{
312			if ($status == '2')
313			{
314				$query->where('protected = 1');
315			}
316			elseif ($status == '3')
317			{
318				$query->where('protected = 0');
319			}
320			else
321			{
322				$query->where('protected = 0')
323					->where('enabled = ' . (int) $status);
324			}
325		}
326
327		if ($type)
328		{
329			$query->where('type = ' . $this->_db->quote($type));
330		}
331
332		if ($clientId != '')
333		{
334			$query->where('client_id = ' . (int) $clientId);
335		}
336
337		if ($folder != '')
338		{
339			$query->where('folder = ' . $this->_db->quote($folder == '*' ? '' : $folder));
340		}
341
342		// Process search filter (extension id).
343		$search = $this->getState('filter.search');
344
345		if (!empty($search) && stripos($search, 'id:') === 0)
346		{
347			$query->where('extension_id = ' . (int) substr($search, 3));
348		}
349
350		// Note: The search for name, ordering and pagination are processed by the parent InstallerModel class (in extension.php).
351
352		return $query;
353	}
354}
355