[ 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_associations 6 * 7 * @copyright (C) 2017 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\Associations\Administrator\Model; 12 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Language\Text; 15 use Joomla\CMS\MVC\Factory\MVCFactoryInterface; 16 use Joomla\CMS\MVC\Model\ListModel; 17 use Joomla\CMS\Table\Table; 18 use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper; 19 use Joomla\Database\Exception\ExecutionFailureException; 20 use Joomla\Database\ParameterType; 21 22 // phpcs:disable PSR1.Files.SideEffects 23 \defined('_JEXEC') or die; 24 // phpcs:enable PSR1.Files.SideEffects 25 26 /** 27 * Methods supporting a list of article records. 28 * 29 * @since 3.7.0 30 */ 31 class AssociationsModel extends ListModel 32 { 33 /** 34 * Override parent constructor. 35 * 36 * @param array $config An optional associative array of configuration settings. 37 * @param MVCFactoryInterface $factory The factory. 38 * 39 * @see \Joomla\CMS\MVC\Model\BaseDatabaseModel 40 * @since 3.7 41 */ 42 public function __construct($config = array(), MVCFactoryInterface $factory = null) 43 { 44 if (empty($config['filter_fields'])) { 45 $config['filter_fields'] = array( 46 'id', 47 'title', 48 'ordering', 49 'itemtype', 50 'language', 51 'association', 52 'menutype', 53 'menutype_title', 54 'level', 55 'state', 56 'category_id', 57 'category_title', 58 'access', 59 'access_level', 60 ); 61 } 62 63 parent::__construct($config, $factory); 64 } 65 66 /** 67 * Method to auto-populate the model state. 68 * 69 * Note. Calling getState in this method will result in recursion. 70 * 71 * @param string $ordering An optional ordering field. 72 * @param string $direction An optional direction (asc|desc). 73 * 74 * @return void 75 * 76 * @since 3.7.0 77 */ 78 protected function populateState($ordering = 'ordering', $direction = 'asc') 79 { 80 $app = Factory::getApplication(); 81 82 $forcedLanguage = $app->input->get('forcedLanguage', '', 'cmd'); 83 $forcedItemType = $app->input->get('forcedItemType', '', 'string'); 84 85 // Adjust the context to support modal layouts. 86 if ($layout = $app->input->get('layout')) { 87 $this->context .= '.' . $layout; 88 } 89 90 // Adjust the context to support forced languages. 91 if ($forcedLanguage) { 92 $this->context .= '.' . $forcedLanguage; 93 } 94 95 // Adjust the context to support forced component item types. 96 if ($forcedItemType) { 97 $this->context .= '.' . $forcedItemType; 98 } 99 100 $this->setState('itemtype', $this->getUserStateFromRequest($this->context . '.itemtype', 'itemtype', '', 'string')); 101 $this->setState('language', $this->getUserStateFromRequest($this->context . '.language', 'language', '', 'string')); 102 103 $this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string')); 104 $this->setState('filter.state', $this->getUserStateFromRequest($this->context . '.filter.state', 'filter_state', '', 'cmd')); 105 $this->setState('filter.category_id', $this->getUserStateFromRequest($this->context . '.filter.category_id', 'filter_category_id', '', 'cmd')); 106 $this->setState('filter.menutype', $this->getUserStateFromRequest($this->context . '.filter.menutype', 'filter_menutype', '', 'string')); 107 $this->setState('filter.access', $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', '', 'string')); 108 $this->setState('filter.level', $this->getUserStateFromRequest($this->context . '.filter.level', 'filter_level', '', 'cmd')); 109 110 // List state information. 111 parent::populateState($ordering, $direction); 112 113 // Force a language. 114 if (!empty($forcedLanguage)) { 115 $this->setState('language', $forcedLanguage); 116 } 117 118 // Force a component item type. 119 if (!empty($forcedItemType)) { 120 $this->setState('itemtype', $forcedItemType); 121 } 122 } 123 124 /** 125 * Method to get a store id based on model configuration state. 126 * 127 * This is necessary because the model is used by the component and 128 * different modules that might need different sets of data or different 129 * ordering requirements. 130 * 131 * @param string $id A prefix for the store id. 132 * 133 * @return string A store id. 134 * 135 * @since 3.7.0 136 */ 137 protected function getStoreId($id = '') 138 { 139 // Compile the store id. 140 $id .= ':' . $this->getState('itemtype'); 141 $id .= ':' . $this->getState('language'); 142 $id .= ':' . $this->getState('filter.search'); 143 $id .= ':' . $this->getState('filter.state'); 144 $id .= ':' . $this->getState('filter.category_id'); 145 $id .= ':' . $this->getState('filter.menutype'); 146 $id .= ':' . $this->getState('filter.access'); 147 $id .= ':' . $this->getState('filter.level'); 148 149 return parent::getStoreId($id); 150 } 151 152 /** 153 * Build an SQL query to load the list data. 154 * 155 * @return \Joomla\Database\DatabaseQuery|boolean 156 * 157 * @since 3.7.0 158 */ 159 protected function getListQuery() 160 { 161 $type = null; 162 163 list($extensionName, $typeName) = explode('.', $this->state->get('itemtype'), 2); 164 165 $extension = AssociationsHelper::getSupportedExtension($extensionName); 166 $types = $extension->get('types'); 167 168 if (\array_key_exists($typeName, $types)) { 169 $type = $types[$typeName]; 170 } 171 172 if (\is_null($type)) { 173 return false; 174 } 175 176 // Create a new query object. 177 $user = Factory::getUser(); 178 $db = $this->getDatabase(); 179 $query = $db->getQuery(true); 180 181 $details = $type->get('details'); 182 183 if (!\array_key_exists('support', $details)) { 184 return false; 185 } 186 187 $support = $details['support']; 188 189 if (!\array_key_exists('fields', $details)) { 190 return false; 191 } 192 193 $fields = $details['fields']; 194 195 // Main query. 196 $query->select($db->quoteName($fields['id'], 'id')) 197 ->select($db->quoteName($fields['title'], 'title')) 198 ->select($db->quoteName($fields['alias'], 'alias')); 199 200 if (!\array_key_exists('tables', $details)) { 201 return false; 202 } 203 204 $tables = $details['tables']; 205 206 foreach ($tables as $key => $table) { 207 $query->from($db->quoteName($table, $key)); 208 } 209 210 if (!\array_key_exists('joins', $details)) { 211 return false; 212 } 213 214 $joins = $details['joins']; 215 216 foreach ($joins as $join) { 217 $query->join($join['type'], $db->quoteName($join['condition'])); 218 } 219 220 // Join over the language. 221 $query->select($db->quoteName($fields['language'], 'language')) 222 ->select($db->quoteName('l.title', 'language_title')) 223 ->select($db->quoteName('l.image', 'language_image')) 224 ->join( 225 'LEFT', 226 $db->quoteName('#__languages', 'l'), 227 $db->quoteName('l.lang_code') . ' = ' . $db->quoteName($fields['language']) 228 ); 229 $extensionNameItem = $extensionName . '.item'; 230 231 // Join over the associations. 232 $query->select('COUNT(' . $db->quoteName('asso2.id') . ') > 1 AS ' . $db->quoteName('association')) 233 ->join( 234 'LEFT', 235 $db->quoteName('#__associations', 'asso'), 236 $db->quoteName('asso.id') . ' = ' . $db->quoteName($fields['id']) 237 . ' AND ' . $db->quoteName('asso.context') . ' = :context' 238 ) 239 ->join( 240 'LEFT', 241 $db->quoteName('#__associations', 'asso2'), 242 $db->quoteName('asso2.key') . ' = ' . $db->quoteName('asso.key') 243 ) 244 ->bind(':context', $extensionNameItem); 245 246 // Prepare the group by clause. 247 $groupby = array( 248 $fields['id'], 249 $fields['title'], 250 $fields['alias'], 251 $fields['language'], 252 'l.title', 253 'l.image', 254 ); 255 256 // Select author for ACL checks. 257 if (!empty($fields['created_user_id'])) { 258 $query->select($db->quoteName($fields['created_user_id'], 'created_user_id')); 259 260 $groupby[] = $fields['created_user_id']; 261 } 262 263 // Select checked out data for check in checkins. 264 if (!empty($fields['checked_out']) && !empty($fields['checked_out_time'])) { 265 $query->select($db->quoteName($fields['checked_out'], 'checked_out')) 266 ->select($db->quoteName($fields['checked_out_time'], 'checked_out_time')); 267 268 // Join over the users. 269 $query->select($db->quoteName('u.name', 'editor')) 270 ->join( 271 'LEFT', 272 $db->quoteName('#__users', 'u'), 273 $db->quoteName('u.id') . ' = ' . $db->quoteName($fields['checked_out']) 274 ); 275 276 $groupby[] = 'u.name'; 277 $groupby[] = $fields['checked_out']; 278 $groupby[] = $fields['checked_out_time']; 279 } 280 281 // If component item type supports ordering, select the ordering also. 282 if (!empty($fields['ordering'])) { 283 $query->select($db->quoteName($fields['ordering'], 'ordering')); 284 285 $groupby[] = $fields['ordering']; 286 } 287 288 // If component item type supports state, select the item state also. 289 if (!empty($fields['state'])) { 290 $query->select($db->quoteName($fields['state'], 'state')); 291 292 $groupby[] = $fields['state']; 293 } 294 295 // If component item type supports level, select the level also. 296 if (!empty($fields['level'])) { 297 $query->select($db->quoteName($fields['level'], 'level')); 298 299 $groupby[] = $fields['level']; 300 } 301 302 // If component item type supports categories, select the category also. 303 if (!empty($fields['catid'])) { 304 $query->select($db->quoteName($fields['catid'], 'catid')); 305 306 // Join over the categories. 307 $query->select($db->quoteName('c.title', 'category_title')) 308 ->join( 309 'LEFT', 310 $db->quoteName('#__categories', 'c'), 311 $db->quoteName('c.id') . ' = ' . $db->quoteName($fields['catid']) 312 ); 313 314 $groupby[] = 'c.title'; 315 $groupby[] = $fields['catid']; 316 } 317 318 // If component item type supports menu type, select the menu type also. 319 if (!empty($fields['menutype'])) { 320 $query->select($db->quoteName($fields['menutype'], 'menutype')); 321 322 // Join over the menu types. 323 $query->select($db->quoteName('mt.title', 'menutype_title')) 324 ->select($db->quoteName('mt.id', 'menutypeid')) 325 ->join( 326 'LEFT', 327 $db->quoteName('#__menu_types', 'mt'), 328 $db->quoteName('mt.menutype') . ' = ' . $db->quoteName($fields['menutype']) 329 ); 330 331 $groupby[] = 'mt.title'; 332 $groupby[] = 'mt.id'; 333 $groupby[] = $fields['menutype']; 334 } 335 336 // If component item type supports access level, select the access level also. 337 if (\array_key_exists('acl', $support) && $support['acl'] == true && !empty($fields['access'])) { 338 $query->select($db->quoteName($fields['access'], 'access')); 339 340 // Join over the access levels. 341 $query->select($db->quoteName('ag.title', 'access_level')) 342 ->join( 343 'LEFT', 344 $db->quoteName('#__viewlevels', 'ag'), 345 $db->quoteName('ag.id') . ' = ' . $db->quoteName($fields['access']) 346 ); 347 348 $groupby[] = 'ag.title'; 349 $groupby[] = $fields['access']; 350 351 // Implement View Level Access. 352 if (!$user->authorise('core.admin', $extensionName)) { 353 $groups = $user->getAuthorisedViewLevels(); 354 $query->whereIn($db->quoteName($fields['access']), $groups); 355 } 356 } 357 358 // If component item type is menus we need to remove the root item and the administrator menu. 359 if ($extensionName === 'com_menus') { 360 $query->where($db->quoteName($fields['id']) . ' > 1') 361 ->where($db->quoteName('a.client_id') . ' = 0'); 362 } 363 364 // If component item type is category we need to remove all other component categories. 365 if ($typeName === 'category') { 366 $query->where($db->quoteName('a.extension') . ' = :extensionname') 367 ->bind(':extensionname', $extensionName); 368 } elseif ($typeNameExploded = explode('.', $typeName)) { 369 if (\count($typeNameExploded) > 1 && array_pop($typeNameExploded) === 'category') { 370 $section = implode('.', $typeNameExploded); 371 $extensionNameSection = $extensionName . '.' . $section; 372 $query->where($db->quoteName('a.extension') . ' = :extensionsection') 373 ->bind(':extensionsection', $extensionNameSection); 374 } 375 } 376 377 // Filter on the language. 378 if ($language = $this->getState('language')) { 379 $query->where($db->quoteName($fields['language']) . ' = :language') 380 ->bind(':language', $language); 381 } 382 383 // Filter by item state. 384 $state = $this->getState('filter.state'); 385 386 if (is_numeric($state)) { 387 $state = (int) $state; 388 $query->where($db->quoteName($fields['state']) . ' = :state') 389 ->bind(':state', $state, ParameterType::INTEGER); 390 } elseif ($state === '') { 391 $query->whereIn($db->quoteName($fields['state']), [0, 1]); 392 } 393 394 // Filter on the category. 395 $baselevel = 1; 396 397 if ($categoryId = $this->getState('filter.category_id')) { 398 $categoryTable = Table::getInstance('Category', 'JTable'); 399 $categoryTable->load($categoryId); 400 $baselevel = (int) $categoryTable->level; 401 402 $lft = (int) $categoryTable->lft; 403 $rgt = (int) $categoryTable->rgt; 404 $query->where($db->quoteName('c.lft') . ' >= :lft') 405 ->where($db->quoteName('c.rgt') . ' <= :rgt') 406 ->bind(':lft', $lft, ParameterType::INTEGER) 407 ->bind(':rgt', $rgt, ParameterType::INTEGER); 408 } 409 410 // Filter on the level. 411 if ($level = $this->getState('filter.level')) { 412 $queryLevel = ((int) $level + (int) $baselevel - 1); 413 $query->where($db->quoteName('a.level') . ' <= :alevel') 414 ->bind(':alevel', $queryLevel, ParameterType::INTEGER); 415 } 416 417 // Filter by menu type. 418 if ($menutype = $this->getState('filter.menutype')) { 419 $query->where($db->quoteName($fields['menutype']) . ' = :menutype2') 420 ->bind(':menutype2', $menutype); 421 } 422 423 // Filter by access level. 424 if ($access = $this->getState('filter.access')) { 425 $access = (int) $access; 426 $query->where($db->quoteName($fields['access']) . ' = :access') 427 ->bind(':access', $access, ParameterType::INTEGER); 428 } 429 430 // Filter by search in name. 431 if ($search = $this->getState('filter.search')) { 432 if (stripos($search, 'id:') === 0) { 433 $search = (int) substr($search, 3); 434 $query->where($db->quoteName($fields['id']) . ' = :searchid') 435 ->bind(':searchid', $search, ParameterType::INTEGER); 436 } else { 437 $search = '%' . str_replace(' ', '%', trim($search)) . '%'; 438 $query->where('(' . $db->quoteName($fields['title']) . ' LIKE :title' 439 . ' OR ' . $db->quoteName($fields['alias']) . ' LIKE :alias)') 440 ->bind(':title', $search) 441 ->bind(':alias', $search); 442 } 443 } 444 445 // Add the group by clause 446 $query->group($db->quoteName($groupby)); 447 448 // Add the list ordering clause 449 $listOrdering = $this->state->get('list.ordering', 'id'); 450 $orderDirn = $this->state->get('list.direction', 'ASC'); 451 452 $query->order($db->escape($listOrdering) . ' ' . $db->escape($orderDirn)); 453 454 return $query; 455 } 456 457 /** 458 * Delete associations from #__associations table. 459 * 460 * @param string $context The associations context. Empty for all. 461 * @param string $key The associations key. Empty for all. 462 * 463 * @return boolean True on success. 464 * 465 * @since 3.7.0 466 */ 467 public function purge($context = '', $key = '') 468 { 469 $app = Factory::getApplication(); 470 $db = $this->getDatabase(); 471 $query = $db->getQuery(true)->delete($db->quoteName('#__associations')); 472 473 // Filter by associations context. 474 if ($context) { 475 $query->where($db->quoteName('context') . ' = :context') 476 ->bind(':context', $context); 477 } 478 479 // Filter by key. 480 if ($key) { 481 $query->where($db->quoteName('key') . ' = :key') 482 ->bind(':key', $key); 483 } 484 485 $db->setQuery($query); 486 487 try { 488 $db->execute(); 489 } catch (ExecutionFailureException $e) { 490 $app->enqueueMessage(Text::_('COM_ASSOCIATIONS_PURGE_FAILED'), 'error'); 491 492 return false; 493 } 494 495 $app->enqueueMessage( 496 Text::_((int) $db->getAffectedRows() > 0 ? 'COM_ASSOCIATIONS_PURGE_SUCCESS' : 'COM_ASSOCIATIONS_PURGE_NONE'), 497 'message' 498 ); 499 500 return true; 501 } 502 503 /** 504 * Delete orphans from the #__associations table. 505 * 506 * @param string $context The associations context. Empty for all. 507 * @param string $key The associations key. Empty for all. 508 * 509 * @return boolean True on success 510 * 511 * @since 3.7.0 512 */ 513 public function clean($context = '', $key = '') 514 { 515 $app = Factory::getApplication(); 516 $db = $this->getDatabase(); 517 $query = $db->getQuery(true) 518 ->select($db->quoteName('key') . ', COUNT(*)') 519 ->from($db->quoteName('#__associations')) 520 ->group($db->quoteName('key')) 521 ->having('COUNT(*) = 1'); 522 523 // Filter by associations context. 524 if ($context) { 525 $query->where($db->quoteName('context') . ' = :context') 526 ->bind(':context', $context); 527 } 528 529 // Filter by key. 530 if ($key) { 531 $query->where($db->quoteName('key') . ' = :key') 532 ->bind(':key', $key); 533 } 534 535 $db->setQuery($query); 536 537 $assocKeys = $db->loadObjectList(); 538 539 $count = 0; 540 541 // We have orphans. Let's delete them. 542 foreach ($assocKeys as $value) { 543 $query->clear() 544 ->delete($db->quoteName('#__associations')) 545 ->where($db->quoteName('key') . ' = :valuekey') 546 ->bind(':valuekey', $value->key); 547 548 $db->setQuery($query); 549 550 try { 551 $db->execute(); 552 } catch (ExecutionFailureException $e) { 553 $app->enqueueMessage(Text::_('COM_ASSOCIATIONS_DELETE_ORPHANS_FAILED'), 'error'); 554 555 return false; 556 } 557 558 $count += (int) $db->getAffectedRows(); 559 } 560 561 $app->enqueueMessage( 562 Text::_($count > 0 ? 'COM_ASSOCIATIONS_DELETE_ORPHANS_SUCCESS' : 'COM_ASSOCIATIONS_DELETE_ORPHANS_NONE'), 563 'message' 564 ); 565 566 return true; 567 } 568 }
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 |