[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Administrator 5 * @subpackage com_modules 6 * 7 * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Component\Modules\Administrator\Model; 12 13 use Joomla\CMS\Component\ComponentHelper; 14 use Joomla\CMS\Factory; 15 use Joomla\CMS\Language\Text; 16 use Joomla\CMS\MVC\Model\ListModel; 17 use Joomla\Database\DatabaseQuery; 18 use Joomla\Database\ParameterType; 19 use Joomla\String\StringHelper; 20 use Joomla\Utilities\ArrayHelper; 21 22 // phpcs:disable PSR1.Files.SideEffects 23 \defined('_JEXEC') or die; 24 // phpcs:enable PSR1.Files.SideEffects 25 26 /** 27 * Modules Component Module Model 28 * 29 * @since 1.5 30 */ 31 class ModulesModel extends ListModel 32 { 33 /** 34 * Constructor. 35 * 36 * @param array $config An optional associative array of configuration settings. 37 * 38 * @see \JController 39 * @since 1.6 40 */ 41 public function __construct($config = array()) 42 { 43 if (empty($config['filter_fields'])) { 44 $config['filter_fields'] = array( 45 'id', 'a.id', 46 'title', 'a.title', 47 'checked_out', 'a.checked_out', 48 'checked_out_time', 'a.checked_out_time', 49 'published', 'a.published', 'state', 50 'access', 'a.access', 51 'ag.title', 'access_level', 52 'ordering', 'a.ordering', 53 'module', 'a.module', 54 'language', 'a.language', 55 'l.title', 'language_title', 56 'publish_up', 'a.publish_up', 57 'publish_down', 'a.publish_down', 58 'client_id', 'a.client_id', 59 'position', 'a.position', 60 'pages', 61 'name', 'e.name', 62 'menuitem', 63 ); 64 } 65 66 parent::__construct($config); 67 } 68 69 /** 70 * Method to auto-populate the model state. 71 * 72 * Note. Calling getState in this method will result in recursion. 73 * 74 * @param string $ordering An optional ordering field. 75 * @param string $direction An optional direction (asc|desc). 76 * 77 * @return void 78 * 79 * @since 1.6 80 */ 81 protected function populateState($ordering = 'a.position', $direction = 'asc') 82 { 83 $app = Factory::getApplication(); 84 85 $layout = $app->input->get('layout', '', 'cmd'); 86 87 // Adjust the context to support modal layouts. 88 if ($layout) { 89 $this->context .= '.' . $layout; 90 } 91 92 // Make context client aware 93 $this->context .= '.' . $app->input->get->getInt('client_id', 0); 94 95 // Load the filter state. 96 $this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string')); 97 $this->setState('filter.position', $this->getUserStateFromRequest($this->context . '.filter.position', 'filter_position', '', 'string')); 98 $this->setState('filter.module', $this->getUserStateFromRequest($this->context . '.filter.module', 'filter_module', '', 'string')); 99 $this->setState('filter.menuitem', $this->getUserStateFromRequest($this->context . '.filter.menuitem', 'filter_menuitem', '', 'cmd')); 100 $this->setState('filter.access', $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', '', 'cmd')); 101 102 // If in modal layout on the frontend, state and language are always forced. 103 if ($app->isClient('site') && $layout === 'modal') { 104 $this->setState('filter.language', 'current'); 105 $this->setState('filter.state', 1); 106 } else { 107 // If in backend (modal or not) we get the same fields from the user request. 108 $this->setState('filter.language', $this->getUserStateFromRequest($this->context . '.filter.language', 'filter_language', '', 'string')); 109 $this->setState('filter.state', $this->getUserStateFromRequest($this->context . '.filter.state', 'filter_state', '', 'string')); 110 } 111 112 // Special case for the client id. 113 if ($app->isClient('site') || $layout === 'modal') { 114 $this->setState('client_id', 0); 115 $clientId = 0; 116 } else { 117 $clientId = (int) $this->getUserStateFromRequest($this->context . '.client_id', 'client_id', 0, 'int'); 118 $clientId = (!in_array($clientId, array(0, 1))) ? 0 : $clientId; 119 $this->setState('client_id', $clientId); 120 } 121 122 // Use a different filter file when client is administrator 123 if ($clientId == 1) { 124 $this->filterFormName = 'filter_modulesadmin'; 125 } 126 127 // Load the parameters. 128 $params = ComponentHelper::getParams('com_modules'); 129 $this->setState('params', $params); 130 131 // List state information. 132 parent::populateState($ordering, $direction); 133 } 134 135 /** 136 * Method to get a store id based on model configuration state. 137 * 138 * This is necessary because the model is used by the component and 139 * different modules that might need different sets of data or different 140 * ordering requirements. 141 * 142 * @param string $id A prefix for the store id. 143 * 144 * @return string A store id. 145 */ 146 protected function getStoreId($id = '') 147 { 148 // Compile the store id. 149 $id .= ':' . $this->getState('client_id'); 150 $id .= ':' . $this->getState('filter.search'); 151 $id .= ':' . $this->getState('filter.state'); 152 $id .= ':' . $this->getState('filter.position'); 153 $id .= ':' . $this->getState('filter.module'); 154 $id .= ':' . $this->getState('filter.menuitem'); 155 $id .= ':' . $this->getState('filter.access'); 156 $id .= ':' . $this->getState('filter.language'); 157 158 return parent::getStoreId($id); 159 } 160 161 /** 162 * Returns an object list 163 * 164 * @param DatabaseQuery $query The query 165 * @param int $limitstart Offset 166 * @param int $limit The number of records 167 * 168 * @return array 169 */ 170 protected function _getList($query, $limitstart = 0, $limit = 0) 171 { 172 $listOrder = $this->getState('list.ordering', 'a.position'); 173 $listDirn = $this->getState('list.direction', 'asc'); 174 175 $db = $this->getDatabase(); 176 177 // If ordering by fields that need translate we need to sort the array of objects after translating them. 178 if (in_array($listOrder, array('pages', 'name'))) { 179 // Fetch the results. 180 $db->setQuery($query); 181 $result = $db->loadObjectList(); 182 183 // Translate the results. 184 $this->translate($result); 185 186 // Sort the array of translated objects. 187 $result = ArrayHelper::sortObjects($result, $listOrder, strtolower($listDirn) == 'desc' ? -1 : 1, true, true); 188 189 // Process pagination. 190 $total = count($result); 191 $this->cache[$this->getStoreId('getTotal')] = $total; 192 193 if ($total < $limitstart) { 194 $limitstart = 0; 195 $this->setState('list.start', 0); 196 } 197 198 return array_slice($result, $limitstart, $limit ?: null); 199 } 200 201 // If ordering by fields that doesn't need translate just order the query. 202 if ($listOrder === 'a.ordering') { 203 $query->order($db->quoteName('a.position') . ' ASC') 204 ->order($db->quoteName($listOrder) . ' ' . $db->escape($listDirn)); 205 } elseif ($listOrder === 'a.position') { 206 $query->order($db->quoteName($listOrder) . ' ' . $db->escape($listDirn)) 207 ->order($db->quoteName('a.ordering') . ' ASC'); 208 } else { 209 $query->order($db->quoteName($listOrder) . ' ' . $db->escape($listDirn)); 210 } 211 212 // Process pagination. 213 $result = parent::_getList($query, $limitstart, $limit); 214 215 // Translate the results. 216 $this->translate($result); 217 218 return $result; 219 } 220 221 /** 222 * Translate a list of objects 223 * 224 * @param array &$items The array of objects 225 * 226 * @return array The array of translated objects 227 */ 228 protected function translate(&$items) 229 { 230 $lang = Factory::getLanguage(); 231 $clientPath = $this->getState('client_id') ? JPATH_ADMINISTRATOR : JPATH_SITE; 232 233 foreach ($items as $item) { 234 $extension = $item->module; 235 $source = $clientPath . "/modules/$extension"; 236 $lang->load("$extension.sys", $clientPath) 237 || $lang->load("$extension.sys", $source); 238 $item->name = Text::_($item->name); 239 240 if (is_null($item->pages)) { 241 $item->pages = Text::_('JNONE'); 242 } elseif ($item->pages < 0) { 243 $item->pages = Text::_('COM_MODULES_ASSIGNED_VARIES_EXCEPT'); 244 } elseif ($item->pages > 0) { 245 $item->pages = Text::_('COM_MODULES_ASSIGNED_VARIES_ONLY'); 246 } else { 247 $item->pages = Text::_('JALL'); 248 } 249 } 250 } 251 252 /** 253 * Build an SQL query to load the list data. 254 * 255 * @return DatabaseQuery 256 */ 257 protected function getListQuery() 258 { 259 // Create a new query object. 260 $db = $this->getDatabase(); 261 $query = $db->getQuery(true); 262 263 // Select the required fields. 264 $query->select( 265 $this->getState( 266 'list.select', 267 'a.id, a.title, a.note, a.position, a.module, a.language,' . 268 'a.checked_out, a.checked_out_time, a.published AS published, e.enabled AS enabled, a.access, a.ordering, a.publish_up, a.publish_down' 269 ) 270 ); 271 272 // From modules table. 273 $query->from($db->quoteName('#__modules', 'a')); 274 275 // Join over the language 276 $query->select($db->quoteName('l.title', 'language_title')) 277 ->select($db->quoteName('l.image', 'language_image')) 278 ->join('LEFT', $db->quoteName('#__languages', 'l') . ' ON ' . $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.language')); 279 280 // Join over the users for the checked out user. 281 $query->select($db->quoteName('uc.name', 'editor')) 282 ->join('LEFT', $db->quoteName('#__users', 'uc') . ' ON ' . $db->quoteName('uc.id') . ' = ' . $db->quoteName('a.checked_out')); 283 284 // Join over the asset groups. 285 $query->select($db->quoteName('ag.title', 'access_level')) 286 ->join('LEFT', $db->quoteName('#__viewlevels', 'ag') . ' ON ' . $db->quoteName('ag.id') . ' = ' . $db->quoteName('a.access')); 287 288 // Join over the module menus 289 $query->select('MIN(mm.menuid) AS pages') 290 ->join('LEFT', $db->quoteName('#__modules_menu', 'mm') . ' ON ' . $db->quoteName('mm.moduleid') . ' = ' . $db->quoteName('a.id')); 291 292 // Join over the extensions 293 $query->select($db->quoteName('e.name', 'name')) 294 ->join('LEFT', $db->quoteName('#__extensions', 'e') . ' ON ' . $db->quoteName('e.element') . ' = ' . $db->quoteName('a.module')); 295 296 // Group (careful with PostgreSQL) 297 $query->group( 298 'a.id, a.title, a.note, a.position, a.module, a.language, a.checked_out, ' 299 . 'a.checked_out_time, a.published, a.access, a.ordering, l.title, l.image, uc.name, ag.title, e.name, ' 300 . 'l.lang_code, uc.id, ag.id, mm.moduleid, e.element, a.publish_up, a.publish_down, e.enabled' 301 ); 302 303 // Filter by client. 304 $clientId = (int) $this->getState('client_id'); 305 $query->where($db->quoteName('a.client_id') . ' = :aclientid') 306 ->where($db->quoteName('e.client_id') . ' = :eclientid') 307 ->bind(':aclientid', $clientId, ParameterType::INTEGER) 308 ->bind(':eclientid', $clientId, ParameterType::INTEGER); 309 310 // Filter by current user access level. 311 $user = Factory::getUser(); 312 313 // Get the current user for authorisation checks 314 if ($user->authorise('core.admin') !== true) { 315 $groups = $user->getAuthorisedViewLevels(); 316 $query->whereIn($db->quoteName('a.access'), $groups); 317 } 318 319 // Filter by access level. 320 if ($access = $this->getState('filter.access')) { 321 $access = (int) $access; 322 $query->where($db->quoteName('a.access') . ' = :access') 323 ->bind(':access', $access, ParameterType::INTEGER); 324 } 325 326 // Filter by published state. 327 $state = $this->getState('filter.state'); 328 329 if (is_numeric($state)) { 330 $state = (int) $state; 331 $query->where($db->quoteName('a.published') . ' = :state') 332 ->bind(':state', $state, ParameterType::INTEGER); 333 } elseif ($state === '') { 334 $query->whereIn($db->quoteName('a.published'), [0, 1]); 335 } 336 337 // Filter by position. 338 if ($position = $this->getState('filter.position')) { 339 $position = ($position === 'none') ? '' : $position; 340 $query->where($db->quoteName('a.position') . ' = :position') 341 ->bind(':position', $position); 342 } 343 344 // Filter by module. 345 if ($module = $this->getState('filter.module')) { 346 $query->where($db->quoteName('a.module') . ' = :module') 347 ->bind(':module', $module); 348 } 349 350 // Filter by menuitem id (only for site client). 351 if ((int) $clientId === 0 && $menuItemId = $this->getState('filter.menuitem')) { 352 // If user selected the modules not assigned to any page (menu item). 353 if ((int) $menuItemId === -1) { 354 $query->having('MIN(' . $db->quoteName('mm.menuid') . ') IS NULL'); 355 } else { 356 // If user selected the modules assigned to some particular page (menu item). 357 // Modules in "All" pages. 358 $subQuery1 = $db->getQuery(true); 359 $subQuery1->select('MIN(' . $db->quoteName('menuid') . ')') 360 ->from($db->quoteName('#__modules_menu')) 361 ->where($db->quoteName('moduleid') . ' = ' . $db->quoteName('a.id')); 362 363 // Modules in "Selected" pages that have the chosen menu item id. 364 $menuItemId = (int) $menuItemId; 365 $minusMenuItemId = $menuItemId * -1; 366 $subQuery2 = $db->getQuery(true); 367 $subQuery2->select($db->quoteName('moduleid')) 368 ->from($db->quoteName('#__modules_menu')) 369 ->where($db->quoteName('menuid') . ' = :menuitemid2'); 370 371 // Modules in "All except selected" pages that doesn't have the chosen menu item id. 372 $subQuery3 = $db->getQuery(true); 373 $subQuery3->select($db->quoteName('moduleid')) 374 ->from($db->quoteName('#__modules_menu')) 375 ->where($db->quoteName('menuid') . ' = :menuitemid3'); 376 377 // Filter by modules assigned to the selected menu item. 378 $query->where('( 379 (' . $subQuery1 . ') = 0 380 OR ((' . $subQuery1 . ') > 0 AND ' . $db->quoteName('a.id') . ' IN (' . $subQuery2 . ')) 381 OR ((' . $subQuery1 . ') < 0 AND ' . $db->quoteName('a.id') . ' NOT IN (' . $subQuery3 . ')) 382 )'); 383 $query->bind(':menuitemid2', $menuItemId, ParameterType::INTEGER); 384 $query->bind(':menuitemid3', $minusMenuItemId, ParameterType::INTEGER); 385 } 386 } 387 388 // Filter by search in title or note or id:. 389 $search = $this->getState('filter.search'); 390 391 if (!empty($search)) { 392 if (stripos($search, 'id:') === 0) { 393 $ids = (int) substr($search, 3); 394 $query->where($db->quoteName('a.id') . ' = :id') 395 ->bind(':id', $ids, ParameterType::INTEGER); 396 } else { 397 $search = '%' . StringHelper::strtolower($search) . '%'; 398 $query->extendWhere( 399 'AND', 400 [ 401 'LOWER(' . $db->quoteName('a.title') . ') LIKE :title', 402 'LOWER(' . $db->quoteName('a.note') . ') LIKE :note', 403 ], 404 'OR' 405 ) 406 ->bind(':title', $search) 407 ->bind(':note', $search); 408 } 409 } 410 411 // Filter on the language. 412 if ($language = $this->getState('filter.language')) { 413 if ($language === 'current') { 414 $language = [Factory::getLanguage()->getTag(), '*']; 415 $query->whereIn($db->quoteName('a.language'), $language, ParameterType::STRING); 416 } else { 417 $query->where($db->quoteName('a.language') . ' = :language') 418 ->bind(':language', $language); 419 } 420 } 421 422 return $query; 423 } 424 425 /** 426 * Manipulate the query to be used to evaluate if this is an Empty State to provide specific conditions for this extension. 427 * 428 * @return DatabaseQuery 429 * 430 * @since 4.0.0 431 */ 432 protected function getEmptyStateQuery() 433 { 434 $query = parent::getEmptyStateQuery(); 435 436 $clientId = (int) $this->getState('client_id'); 437 438 $query->where($this->getDatabase()->quoteName('a.client_id') . ' = :client_id') 439 ->bind(':client_id', $clientId, ParameterType::INTEGER); 440 441 return $query; 442 } 443 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |