[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Helper; 11 12 use Joomla\CMS\Component\ComponentHelper; 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Table\CoreContent; 15 use Joomla\CMS\Table\Table; 16 use Joomla\CMS\Table\TableInterface; 17 use Joomla\CMS\UCM\UCMContent; 18 use Joomla\CMS\UCM\UCMType; 19 use Joomla\Database\DatabaseDriver; 20 use Joomla\Database\ParameterType; 21 use Joomla\Utilities\ArrayHelper; 22 23 // phpcs:disable PSR1.Files.SideEffects 24 \defined('JPATH_PLATFORM') or die; 25 // phpcs:enable PSR1.Files.SideEffects 26 27 /** 28 * Tags helper class, provides methods to perform various tasks relevant 29 * tagging of content. 30 * 31 * @since 3.1 32 */ 33 class TagsHelper extends CMSHelper 34 { 35 /** 36 * Helper object for storing and deleting tag information. 37 * 38 * @var boolean 39 * @since 3.1 40 */ 41 protected $tagsChanged = false; 42 43 /** 44 * Whether up replace all tags or just add tags 45 * 46 * @var boolean 47 * @since 3.1 48 */ 49 protected $replaceTags = false; 50 51 /** 52 * Alias for querying mapping and content type table. 53 * 54 * @var string 55 * @since 3.1 56 */ 57 public $typeAlias; 58 59 /** 60 * Array of item tags. 61 * 62 * @var array 63 * @since 3.1 64 */ 65 public $itemTags; 66 67 /** 68 * Method to add tag rows to mapping table. 69 * 70 * @param integer $ucmId ID of the #__ucm_content item being tagged 71 * @param TableInterface $table Table object being tagged 72 * @param array $tags Array of tags to be applied. 73 * 74 * @return boolean true on success, otherwise false. 75 * 76 * @since 3.1 77 */ 78 public function addTagMapping($ucmId, TableInterface $table, $tags = array()) 79 { 80 $db = $table->getDbo(); 81 $key = $table->getKeyName(); 82 $item = $table->$key; 83 $ucm = new UCMType($this->typeAlias, $db); 84 $typeId = $ucm->getTypeId(); 85 86 // Insert the new tag maps 87 if (strpos('#', implode(',', $tags)) === false) { 88 $tags = self::createTagsFromField($tags); 89 } 90 91 // Prevent saving duplicate tags 92 $tags = array_values(array_unique($tags)); 93 94 if (!$tags) { 95 return true; 96 } 97 98 $query = $db->getQuery(true); 99 $query->insert('#__contentitem_tag_map'); 100 $query->columns( 101 [ 102 $db->quoteName('core_content_id'), 103 $db->quoteName('content_item_id'), 104 $db->quoteName('tag_id'), 105 $db->quoteName('type_id'), 106 $db->quoteName('type_alias'), 107 $db->quoteName('tag_date'), 108 ] 109 ); 110 111 foreach ($tags as $tag) { 112 $query->values( 113 implode( 114 ',', 115 array_merge( 116 $query->bindArray([(int) $ucmId, (int) $item, (int) $tag, (int) $typeId]), 117 $query->bindArray([$this->typeAlias], ParameterType::STRING), 118 [$query->currentTimestamp()] 119 ) 120 ) 121 ); 122 } 123 124 $db->setQuery($query); 125 126 return (bool) $db->execute(); 127 } 128 129 /** 130 * Function that converts tags paths into paths of names 131 * 132 * @param array $tags Array of tags 133 * 134 * @return array 135 * 136 * @since 3.1 137 */ 138 public static function convertPathsToNames($tags) 139 { 140 // We will replace path aliases with tag names 141 if ($tags) { 142 // Create an array with all the aliases of the results 143 $aliases = array(); 144 145 foreach ($tags as $tag) { 146 if (!empty($tag->path)) { 147 if ($pathParts = explode('/', $tag->path)) { 148 $aliases = array_merge($aliases, $pathParts); 149 } 150 } 151 } 152 153 // Get the aliases titles in one single query and map the results 154 if ($aliases) { 155 // Remove duplicates 156 $aliases = array_values(array_unique($aliases)); 157 158 $db = Factory::getDbo(); 159 160 $query = $db->getQuery(true) 161 ->select( 162 [ 163 $db->quoteName('alias'), 164 $db->quoteName('title'), 165 ] 166 ) 167 ->from($db->quoteName('#__tags')) 168 ->whereIn($db->quoteName('alias'), $aliases, ParameterType::STRING); 169 $db->setQuery($query); 170 171 try { 172 $aliasesMapper = $db->loadAssocList('alias'); 173 } catch (\RuntimeException $e) { 174 return false; 175 } 176 177 // Rebuild the items path 178 if ($aliasesMapper) { 179 foreach ($tags as $tag) { 180 $namesPath = array(); 181 182 if (!empty($tag->path)) { 183 if ($pathParts = explode('/', $tag->path)) { 184 foreach ($pathParts as $alias) { 185 if (isset($aliasesMapper[$alias])) { 186 $namesPath[] = $aliasesMapper[$alias]['title']; 187 } else { 188 $namesPath[] = $alias; 189 } 190 } 191 192 $tag->text = implode('/', $namesPath); 193 } 194 } 195 } 196 } 197 } 198 } 199 200 return $tags; 201 } 202 203 /** 204 * Create any new tags by looking for #new# in the strings 205 * 206 * @param array $tags Tags text array from the field 207 * 208 * @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false. 209 * 210 * @since 3.1 211 */ 212 public function createTagsFromField($tags) 213 { 214 if (empty($tags) || $tags[0] == '') { 215 return; 216 } else { 217 // We will use the tags table to store them 218 $tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); 219 $newTags = array(); 220 $canCreate = Factory::getUser()->authorise('core.create', 'com_tags'); 221 222 foreach ($tags as $key => $tag) { 223 // User is not allowed to create tags, so don't create. 224 if (!$canCreate && strpos($tag, '#new#') !== false) { 225 continue; 226 } 227 228 // Remove the #new# prefix that identifies new tags 229 $tagText = str_replace('#new#', '', $tag); 230 231 if ($tagText === $tag) { 232 $newTags[] = (int) $tag; 233 } else { 234 // Clear old data if exist 235 $tagTable->reset(); 236 237 // Try to load the selected tag 238 if ($tagTable->load(array('title' => $tagText))) { 239 $newTags[] = (int) $tagTable->id; 240 } else { 241 // Prepare tag data 242 $tagTable->id = 0; 243 $tagTable->title = $tagText; 244 $tagTable->published = 1; 245 $tagTable->description = ''; 246 247 // $tagTable->language = property_exists ($item, 'language') ? $item->language : '*'; 248 $tagTable->language = '*'; 249 $tagTable->access = 1; 250 251 // Make this item a child of the root tag 252 $tagTable->setLocation($tagTable->getRootId(), 'last-child'); 253 254 // Try to store tag 255 if ($tagTable->check()) { 256 // Assign the alias as path (autogenerated tags have always level 1) 257 $tagTable->path = $tagTable->alias; 258 259 if ($tagTable->store()) { 260 $newTags[] = (int) $tagTable->id; 261 } 262 } 263 } 264 } 265 } 266 267 // At this point $tags is an array of all tag ids 268 $this->tags = $newTags; 269 $result = $newTags; 270 } 271 272 return $result; 273 } 274 275 /** 276 * Method to delete the tag mappings and #__ucm_content record for for an item 277 * 278 * @param TableInterface $table Table object of content table where delete occurred 279 * @param integer|array $contentItemId ID of the content item. Or an array of key/value pairs with array key 280 * being a primary key name and value being the content item ID. Note 281 * multiple primary keys are not supported 282 * 283 * @return boolean true on success, false on failure 284 * 285 * @since 3.1 286 * @throws \InvalidArgumentException 287 */ 288 public function deleteTagData(TableInterface $table, $contentItemId) 289 { 290 $key = $table->getKeyName(); 291 292 if (!\is_array($contentItemId)) { 293 $contentItemId = array($key => $contentItemId); 294 } 295 296 // If we have multiple items for the content item primary key we currently don't support this so 297 // throw an InvalidArgumentException for now 298 if (\count($contentItemId) != 1) { 299 throw new \InvalidArgumentException('Multiple primary keys are not supported as a content item id'); 300 } 301 302 $result = $this->unTagItem($contentItemId[$key], $table); 303 304 /** @var CoreContent $ucmContentTable */ 305 $ucmContentTable = Table::getInstance('Corecontent'); 306 307 return $result && $ucmContentTable->deleteByContentId($contentItemId[$key], $this->typeAlias); 308 } 309 310 /** 311 * Method to get a list of tags for an item, optionally with the tag data. 312 * 313 * @param string $contentType Content type alias. Dot separated. 314 * @param integer $id Id of the item to retrieve tags for. 315 * @param boolean $getTagData If true, data from the tags table will be included, defaults to true. 316 * 317 * @return array Array of of tag objects 318 * 319 * @since 3.1 320 */ 321 public function getItemTags($contentType, $id, $getTagData = true) 322 { 323 // Cast as integer until method is typehinted. 324 $id = (int) $id; 325 326 // Initialize some variables. 327 $db = Factory::getDbo(); 328 $query = $db->getQuery(true) 329 ->select($db->quoteName('m.tag_id')) 330 ->from($db->quoteName('#__contentitem_tag_map', 'm')) 331 ->where( 332 [ 333 $db->quoteName('m.type_alias') . ' = :contentType', 334 $db->quoteName('m.content_item_id') . ' = :id', 335 $db->quoteName('t.published') . ' = 1', 336 ] 337 ) 338 ->bind(':contentType', $contentType) 339 ->bind(':id', $id, ParameterType::INTEGER); 340 341 $user = Factory::getUser(); 342 $groups = $user->getAuthorisedViewLevels(); 343 344 $query->whereIn($db->quoteName('t.access'), $groups); 345 346 // Optionally filter on language 347 $language = ComponentHelper::getParams('com_tags')->get('tag_list_language_filter', 'all'); 348 349 if ($language !== 'all') { 350 if ($language === 'current_language') { 351 $language = $this->getCurrentLanguage(); 352 } 353 354 $query->whereIn($db->quoteName('language'), [$language, '*'], ParameterType::STRING); 355 } 356 357 if ($getTagData) { 358 $query->select($db->quoteName('t') . '.*'); 359 } 360 361 $query->join('INNER', $db->quoteName('#__tags', 't'), $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id')); 362 363 $db->setQuery($query); 364 $this->itemTags = $db->loadObjectList(); 365 366 return $this->itemTags; 367 } 368 369 /** 370 * Method to get a list of tags for multiple items, optionally with the tag data. 371 * 372 * @param string $contentType Content type alias. Dot separated. 373 * @param array $ids Id of the item to retrieve tags for. 374 * @param boolean $getTagData If true, data from the tags table will be included, defaults to true. 375 * 376 * @return array Array of of tag objects grouped by Id. 377 * 378 * @since 4.2.0 379 */ 380 public function getMultipleItemTags($contentType, array $ids, $getTagData = true) 381 { 382 $data = []; 383 384 $ids = array_map('intval', $ids); 385 386 /** @var DatabaseDriver $db */ 387 $db = Factory::getContainer()->get('DatabaseDriver'); 388 389 $query = $db->getQuery(true) 390 ->select($db->quoteName(['m.tag_id', 'm.content_item_id'])) 391 ->from($db->quoteName('#__contentitem_tag_map', 'm')) 392 ->where( 393 [ 394 $db->quoteName('m.type_alias') . ' = :contentType', 395 $db->quoteName('t.published') . ' = 1', 396 ] 397 ) 398 ->whereIn($db->quoteName('m.content_item_id'), $ids) 399 ->bind(':contentType', $contentType); 400 401 $query->join('INNER', $db->quoteName('#__tags', 't'), $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id')); 402 403 $groups = Factory::getUser()->getAuthorisedViewLevels(); 404 405 $query->whereIn($db->quoteName('t.access'), $groups); 406 407 // Optionally filter on language 408 $language = ComponentHelper::getParams('com_tags')->get('tag_list_language_filter', 'all'); 409 410 if ($language !== 'all') { 411 if ($language === 'current_language') { 412 $language = $this->getCurrentLanguage(); 413 } 414 415 $query->whereIn($db->quoteName('language'), [$language, '*'], ParameterType::STRING); 416 } 417 418 if ($getTagData) { 419 $query->select($db->quoteName('t') . '.*'); 420 } 421 422 $db->setQuery($query); 423 424 $rows = $db->loadObjectList(); 425 426 // Group data by item Id. 427 foreach ($rows as $row) { 428 $data[$row->content_item_id][] = $row; 429 unset($row->content_item_id); 430 } 431 432 return $data; 433 } 434 435 /** 436 * Method to get a list of tags for a given item. 437 * Normally used for displaying a list of tags within a layout 438 * 439 * @param mixed $ids The id or array of ids (primary key) of the item to be tagged. 440 * @param string $prefix Dot separated string with the option and view to be used for a url. 441 * 442 * @return string Comma separated list of tag Ids. 443 * 444 * @since 3.1 445 */ 446 public function getTagIds($ids, $prefix) 447 { 448 if (empty($ids)) { 449 return; 450 } 451 452 /** 453 * Ids possible formats: 454 * --------------------- 455 * $id = 1; 456 * $id = array(1,2); 457 * $id = array('1,3,4,19'); 458 * $id = '1,3'; 459 */ 460 $ids = (array) $ids; 461 $ids = implode(',', $ids); 462 $ids = explode(',', $ids); 463 $ids = ArrayHelper::toInteger($ids); 464 465 $db = Factory::getDbo(); 466 467 // Load the tags. 468 $query = $db->getQuery(true) 469 ->select($db->quoteName('t.id')) 470 ->from($db->quoteName('#__tags', 't')) 471 ->join('INNER', $db->quoteName('#__contentitem_tag_map', 'm'), $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id')) 472 ->where($db->quoteName('m.type_alias') . ' = :prefix') 473 ->whereIn($db->quoteName('m.content_item_id'), $ids) 474 ->bind(':prefix', $prefix); 475 476 $db->setQuery($query); 477 478 // Add the tags to the content data. 479 $tagsList = $db->loadColumn(); 480 $this->tags = implode(',', $tagsList); 481 482 return $this->tags; 483 } 484 485 /** 486 * Method to get a query to retrieve a detailed list of items for a tag. 487 * 488 * @param mixed $tagId Tag or array of tags to be matched 489 * @param mixed $typesr Null, type or array of type aliases for content types to be included in the results 490 * @param boolean $includeChildren True to include the results from child tags 491 * @param string $orderByOption Column to order the results by 492 * @param string $orderDir Direction to sort the results in 493 * @param boolean $anyOrAll True to include items matching at least one tag, false to include 494 * items all tags in the array. 495 * @param string $languageFilter Optional filter on language. Options are 'all', 'current' or any string. 496 * @param string $stateFilter Optional filtering on publication state, defaults to published or unpublished. 497 * 498 * @return \Joomla\Database\DatabaseQuery Query to retrieve a list of tags 499 * 500 * @since 3.1 501 */ 502 public function getTagItemsQuery( 503 $tagId, 504 $typesr = null, 505 $includeChildren = false, 506 $orderByOption = 'c.core_title', 507 $orderDir = 'ASC', 508 $anyOrAll = true, 509 $languageFilter = 'all', 510 $stateFilter = '0,1' 511 ) { 512 // Create a new query object. 513 $db = Factory::getDbo(); 514 $query = $db->getQuery(true); 515 $user = Factory::getUser(); 516 $nullDate = $db->getNullDate(); 517 $nowDate = Factory::getDate()->toSql(); 518 519 // Force ids to array and sanitize 520 $tagIds = (array) $tagId; 521 $tagIds = implode(',', $tagIds); 522 $tagIds = explode(',', $tagIds); 523 $tagIds = ArrayHelper::toInteger($tagIds); 524 525 $ntagsr = \count($tagIds); 526 527 // If we want to include children we have to adjust the list of tags. 528 // We do not search child tags when the match all option is selected. 529 if ($includeChildren) { 530 $tagTreeArray = array(); 531 532 foreach ($tagIds as $tag) { 533 $this->getTagTreeArray($tag, $tagTreeArray); 534 } 535 536 $tagIds = array_values(array_unique(array_merge($tagIds, $tagTreeArray))); 537 } 538 539 // Sanitize filter states 540 $stateFilters = explode(',', $stateFilter); 541 $stateFilters = ArrayHelper::toInteger($stateFilters); 542 543 // M is the mapping table. C is the core_content table. Ct is the content_types table. 544 $query->select( 545 [ 546 $db->quoteName('m.type_alias'), 547 $db->quoteName('m.content_item_id'), 548 $db->quoteName('m.core_content_id'), 549 'COUNT(' . $db->quoteName('m.tag_id') . ') AS ' . $db->quoteName('match_count'), 550 'MAX(' . $db->quoteName('m.tag_date') . ') AS ' . $db->quoteName('tag_date'), 551 'MAX(' . $db->quoteName('c.core_title') . ') AS ' . $db->quoteName('core_title'), 552 'MAX(' . $db->quoteName('c.core_params') . ') AS ' . $db->quoteName('core_params'), 553 'MAX(' . $db->quoteName('c.core_alias') . ') AS ' . $db->quoteName('core_alias'), 554 'MAX(' . $db->quoteName('c.core_body') . ') AS ' . $db->quoteName('core_body'), 555 'MAX(' . $db->quoteName('c.core_state') . ') AS ' . $db->quoteName('core_state'), 556 'MAX(' . $db->quoteName('c.core_access') . ') AS ' . $db->quoteName('core_access'), 557 'MAX(' . $db->quoteName('c.core_metadata') . ') AS ' . $db->quoteName('core_metadata'), 558 'MAX(' . $db->quoteName('c.core_created_user_id') . ') AS ' . $db->quoteName('core_created_user_id'), 559 'MAX(' . $db->quoteName('c.core_created_by_alias') . ') AS' . $db->quoteName('core_created_by_alias'), 560 'MAX(' . $db->quoteName('c.core_created_time') . ') AS ' . $db->quoteName('core_created_time'), 561 'MAX(' . $db->quoteName('c.core_images') . ') AS ' . $db->quoteName('core_images'), 562 'CASE WHEN ' . $db->quoteName('c.core_modified_time') . ' = :nullDate THEN ' . $db->quoteName('c.core_created_time') 563 . ' ELSE ' . $db->quoteName('c.core_modified_time') . ' END AS ' . $db->quoteName('core_modified_time'), 564 'MAX(' . $db->quoteName('c.core_language') . ') AS ' . $db->quoteName('core_language'), 565 'MAX(' . $db->quoteName('c.core_catid') . ') AS ' . $db->quoteName('core_catid'), 566 'MAX(' . $db->quoteName('c.core_publish_up') . ') AS ' . $db->quoteName('core_publish_up'), 567 'MAX(' . $db->quoteName('c.core_publish_down') . ') AS ' . $db->quoteName('core_publish_down'), 568 'MAX(' . $db->quoteName('ct.type_title') . ') AS ' . $db->quoteName('content_type_title'), 569 'MAX(' . $db->quoteName('ct.router') . ') AS ' . $db->quoteName('router'), 570 'CASE WHEN ' . $db->quoteName('c.core_created_by_alias') . ' > ' . $db->quote(' ') 571 . ' THEN ' . $db->quoteName('c.core_created_by_alias') . ' ELSE ' . $db->quoteName('ua.name') . ' END AS ' . $db->quoteName('author'), 572 $db->quoteName('ua.email', 'author_email'), 573 ] 574 ) 575 ->bind(':nullDate', $nullDate) 576 ->from($db->quoteName('#__contentitem_tag_map', 'm')) 577 ->join( 578 'INNER', 579 $db->quoteName('#__ucm_content', 'c'), 580 $db->quoteName('m.type_alias') . ' = ' . $db->quoteName('c.core_type_alias') 581 . ' AND ' . $db->quoteName('m.core_content_id') . ' = ' . $db->quoteName('c.core_content_id') 582 ) 583 ->join('INNER', $db->quoteName('#__content_types', 'ct'), $db->quoteName('ct.type_alias') . ' = ' . $db->quoteName('m.type_alias')); 584 585 // Join over categories to get only tags from published categories 586 $query->join('LEFT', $db->quoteName('#__categories', 'tc'), $db->quoteName('tc.id') . ' = ' . $db->quoteName('c.core_catid')); 587 588 // Join over the users for the author and email 589 $query->join('LEFT', $db->quoteName('#__users', 'ua'), $db->quoteName('ua.id') . ' = ' . $db->quoteName('c.core_created_user_id')) 590 ->whereIn($db->quoteName('c.core_state'), $stateFilters) 591 ->whereIn($db->quoteName('m.tag_id'), $tagIds) 592 ->extendWhere( 593 'AND', 594 [ 595 $db->quoteName('c.core_catid') . ' = 0', 596 $db->quoteName('tc.published') . ' = 1', 597 ], 598 'OR' 599 ); 600 601 // Get the type data, limited to types in the request if there are any specified. 602 $typesarray = self::getTypes('assocList', $typesr, false); 603 $typeAliases = \array_column($typesarray, 'type_alias'); 604 $query->whereIn($db->quoteName('m.type_alias'), $typeAliases, ParameterType::STRING); 605 606 $groups = array_values(array_unique($user->getAuthorisedViewLevels())); 607 $groups[] = 0; 608 $query->whereIn($db->quoteName('c.core_access'), $groups); 609 610 if (!\in_array(0, $stateFilters, true)) { 611 $query->extendWhere( 612 'AND', 613 [ 614 $db->quoteName('c.core_publish_up') . ' = :nullDate1', 615 $db->quoteName('c.core_publish_up') . ' IS NULL', 616 $db->quoteName('c.core_publish_up') . ' <= :nowDate1', 617 ], 618 'OR' 619 ) 620 ->extendWhere( 621 'AND', 622 [ 623 $db->quoteName('c.core_publish_down') . ' = :nullDate2', 624 $db->quoteName('c.core_publish_down') . ' IS NULL', 625 $db->quoteName('c.core_publish_down') . ' >= :nowDate2', 626 ], 627 'OR' 628 ) 629 ->bind([':nullDate1', ':nullDate2'], $nullDate) 630 ->bind([':nowDate1', ':nowDate2'], $nowDate); 631 } 632 633 // Optionally filter on language 634 if ($languageFilter !== 'all') { 635 if ($languageFilter === 'current_language') { 636 $languageFilter = $this->getCurrentLanguage(); 637 } 638 639 $query->whereIn($db->quoteName('c.core_language'), [$languageFilter, '*'], ParameterType::STRING); 640 } 641 642 $query->group( 643 [ 644 $db->quoteName('m.type_alias'), 645 $db->quoteName('m.content_item_id'), 646 $db->quoteName('m.core_content_id'), 647 $db->quoteName('core_modified_time'), 648 $db->quoteName('core_created_time'), 649 $db->quoteName('core_created_by_alias'), 650 $db->quoteName('author'), 651 $db->quoteName('author_email'), 652 ] 653 ); 654 655 // Use HAVING if matching all tags and we are matching more than one tag. 656 if ($ntagsr > 1 && $anyOrAll != 1 && $includeChildren != 1) { 657 // The number of results should equal the number of tags requested. 658 $query->having('COUNT(' . $db->quoteName('m.tag_id') . ') = :ntagsr') 659 ->bind(':ntagsr', $ntagsr, ParameterType::INTEGER); 660 } 661 662 // Set up the order by using the option chosen 663 if ($orderByOption === 'match_count') { 664 $orderBy = 'COUNT(' . $db->quoteName('m.tag_id') . ')'; 665 } else { 666 $orderBy = 'MAX(' . $db->quoteName($orderByOption) . ')'; 667 } 668 669 $query->order($orderBy . ' ' . $orderDir); 670 671 return $query; 672 } 673 674 /** 675 * Function that converts tag ids to their tag names 676 * 677 * @param array $tagIds Array of integer tag ids. 678 * 679 * @return array An array of tag names. 680 * 681 * @since 3.1 682 */ 683 public function getTagNames($tagIds) 684 { 685 $tagNames = array(); 686 687 if (\is_array($tagIds) && \count($tagIds) > 0) { 688 $tagIds = ArrayHelper::toInteger($tagIds); 689 690 $db = Factory::getDbo(); 691 $query = $db->getQuery(true) 692 ->select($db->quoteName('title')) 693 ->from($db->quoteName('#__tags')) 694 ->whereIn($db->quoteName('id'), $tagIds) 695 ->order($db->quoteName('title')); 696 697 $db->setQuery($query); 698 $tagNames = $db->loadColumn(); 699 } 700 701 return $tagNames; 702 } 703 704 /** 705 * Method to get an array of tag ids for the current tag and its children 706 * 707 * @param integer $id An optional ID 708 * @param array &$tagTreeArray Array containing the tag tree 709 * 710 * @return mixed 711 * 712 * @since 3.1 713 */ 714 public function getTagTreeArray($id, &$tagTreeArray = array()) 715 { 716 // Get a level row instance. 717 $table = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); 718 719 if ($table->isLeaf($id)) { 720 $tagTreeArray[] = $id; 721 722 return $tagTreeArray; 723 } 724 725 $tagTree = $table->getTree($id); 726 727 // Attempt to load the tree 728 if ($tagTree) { 729 foreach ($tagTree as $tag) { 730 $tagTreeArray[] = $tag->id; 731 } 732 733 return $tagTreeArray; 734 } 735 } 736 737 /** 738 * Method to get a list of types with associated data. 739 * 740 * @param string $arrayType Optionally specify that the returned list consist of objects, associative arrays, or arrays. 741 * Options are: rowList, assocList, and objectList 742 * @param array $selectTypes Optional array of type ids or aliases to limit the results to. Often from a request. 743 * @param boolean $useAlias If true, the alias is used to match, if false the type_id is used. 744 * 745 * @return array Array of of types 746 * 747 * @since 3.1 748 */ 749 public static function getTypes($arrayType = 'objectList', $selectTypes = null, $useAlias = true) 750 { 751 // Initialize some variables. 752 $db = Factory::getDbo(); 753 $query = $db->getQuery(true) 754 ->select('*'); 755 756 if (!empty($selectTypes)) { 757 $selectTypes = (array) $selectTypes; 758 759 if ($useAlias) { 760 $query->whereIn($db->quoteName('type_alias'), $selectTypes, ParameterType::STRING); 761 } else { 762 $selectTypes = ArrayHelper::toInteger($selectTypes); 763 764 $query->whereIn($db->quoteName('type_id'), $selectTypes); 765 } 766 } 767 768 $query->from($db->quoteName('#__content_types')); 769 770 $db->setQuery($query); 771 772 switch ($arrayType) { 773 case 'assocList': 774 $types = $db->loadAssocList(); 775 break; 776 777 case 'rowList': 778 $types = $db->loadRowList(); 779 break; 780 781 case 'objectList': 782 default: 783 $types = $db->loadObjectList(); 784 break; 785 } 786 787 return $types; 788 } 789 790 /** 791 * Function that handles saving tags used in a table class after a store() 792 * 793 * @param TableInterface $table Table being processed 794 * @param array $newTags Array of new tags 795 * @param boolean $replace Flag indicating if all existing tags should be replaced 796 * 797 * @return boolean 798 * 799 * @since 3.1 800 */ 801 public function postStoreProcess(TableInterface $table, $newTags = array(), $replace = true) 802 { 803 if (!empty($table->newTags) && empty($newTags)) { 804 $newTags = $table->newTags; 805 } 806 807 // If existing row, check to see if tags have changed. 808 $newTable = clone $table; 809 $newTable->reset(); 810 811 $result = true; 812 813 // Process ucm_content and ucm_base if either tags have changed or we have some tags. 814 if ($this->tagsChanged || (!empty($newTags) && $newTags[0] != '')) { 815 if (!$newTags && $replace == true) { 816 // Delete all tags data 817 $key = $table->getKeyName(); 818 $result = $this->deleteTagData($table, $table->$key); 819 } else { 820 // Process the tags 821 $data = $this->getRowData($table); 822 $ucmContentTable = Table::getInstance('Corecontent'); 823 824 $ucm = new UCMContent($table, $this->typeAlias); 825 $ucmData = $data ? $ucm->mapData($data) : $ucm->ucmData; 826 827 $primaryId = $ucm->getPrimaryKey($ucmData['common']['core_type_id'], $ucmData['common']['core_content_item_id']); 828 $result = $ucmContentTable->load($primaryId); 829 $result = $result && $ucmContentTable->bind($ucmData['common']); 830 $result = $result && $ucmContentTable->check(); 831 $result = $result && $ucmContentTable->store(); 832 $ucmId = $ucmContentTable->core_content_id; 833 834 // Store the tag data if the article data was saved and run related methods. 835 $result = $result && $this->tagItem($ucmId, $table, $newTags, $replace); 836 } 837 } 838 839 return $result; 840 } 841 842 /** 843 * Function that preProcesses data from a table prior to a store() to ensure proper tag handling 844 * 845 * @param TableInterface $table Table being processed 846 * @param array $newTags Array of new tags 847 * 848 * @return null 849 * 850 * @since 3.1 851 */ 852 public function preStoreProcess(TableInterface $table, $newTags = array()) 853 { 854 if ($newTags != array()) { 855 $this->newTags = $newTags; 856 } 857 858 // If existing row, check to see if tags have changed. 859 $oldTable = clone $table; 860 $oldTable->reset(); 861 $key = $oldTable->getKeyName(); 862 $typeAlias = $this->typeAlias; 863 864 if ($oldTable->$key && $oldTable->load()) { 865 $this->oldTags = $this->getTagIds($oldTable->$key, $typeAlias); 866 } 867 868 // New items with no tags bypass this step. 869 if ((!empty($newTags) && \is_string($newTags) || (isset($newTags[0]) && $newTags[0] != '')) || isset($this->oldTags)) { 870 if (\is_array($newTags)) { 871 $newTags = implode(',', $newTags); 872 } 873 874 // We need to process tags if the tags have changed or if we have a new row 875 $this->tagsChanged = (empty($this->oldTags) && !empty($newTags)) || (!empty($this->oldTags) && $this->oldTags != $newTags) || !$table->$key; 876 } 877 } 878 879 /** 880 * Function to search tags 881 * 882 * @param array $filters Filter to apply to the search 883 * 884 * @return array 885 * 886 * @since 3.1 887 */ 888 public static function searchTags($filters = array()) 889 { 890 $db = Factory::getDbo(); 891 $query = $db->getQuery(true) 892 ->select( 893 [ 894 $db->quoteName('a.id', 'value'), 895 $db->quoteName('a.path', 'text'), 896 $db->quoteName('a.path'), 897 ] 898 ) 899 ->from($db->quoteName('#__tags', 'a')) 900 ->join( 901 'LEFT', 902 $db->quoteName('#__tags', 'b'), 903 $db->quoteName('a.lft') . ' > ' . $db->quoteName('b.lft') . ' AND ' . $db->quoteName('a.rgt') . ' < ' . $db->quoteName('b.rgt') 904 ); 905 906 // Do not return root 907 $query->where($db->quoteName('a.alias') . ' <> ' . $db->quote('root')); 908 909 // Filter language 910 if (!empty($filters['flanguage'])) { 911 $query->whereIn($db->quoteName('a.language'), [$filters['flanguage'], '*'], ParameterType::STRING); 912 } 913 914 // Search in title or path 915 if (!empty($filters['like'])) { 916 $search = '%' . $filters['like'] . '%'; 917 $query->extendWhere( 918 'AND', 919 [ 920 $db->quoteName('a.title') . ' LIKE :search1', 921 $db->quoteName('a.path') . ' LIKE :search2', 922 ], 923 'OR' 924 ) 925 ->bind([':search1', ':search2'], $search); 926 } 927 928 // Filter title 929 if (!empty($filters['title'])) { 930 $query->where($db->quoteName('a.title') . ' = :title') 931 ->bind(':title', $filters['title']); 932 } 933 934 // Filter on the published state 935 if (isset($filters['published']) && is_numeric($filters['published'])) { 936 $published = (int) $filters['published']; 937 $query->where($db->quoteName('a.published') . ' = :published') 938 ->bind(':published', $published, ParameterType::INTEGER); 939 } 940 941 // Filter on the access level 942 if (isset($filters['access']) && \is_array($filters['access']) && \count($filters['access'])) { 943 $groups = ArrayHelper::toInteger($filters['access']); 944 $query->whereIn($db->quoteName('a.access'), $groups); 945 } 946 947 // Filter by parent_id 948 if (isset($filters['parent_id']) && is_numeric($filters['parent_id'])) { 949 $tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); 950 951 if ($children = $tagTable->getTree($filters['parent_id'])) { 952 $childrenIds = \array_column($children, 'id'); 953 954 $query->whereIn($db->quoteName('a.id'), $childrenIds); 955 } 956 } 957 958 $query->group( 959 [ 960 $db->quoteName('a.id'), 961 $db->quoteName('a.title'), 962 $db->quoteName('a.level'), 963 $db->quoteName('a.lft'), 964 $db->quoteName('a.rgt'), 965 $db->quoteName('a.parent_id'), 966 $db->quoteName('a.published'), 967 $db->quoteName('a.path'), 968 ] 969 ) 970 ->order($db->quoteName('a.lft') . ' ASC'); 971 972 // Get the options. 973 $db->setQuery($query); 974 975 try { 976 $results = $db->loadObjectList(); 977 } catch (\RuntimeException $e) { 978 return array(); 979 } 980 981 // We will replace path aliases with tag names 982 return self::convertPathsToNames($results); 983 } 984 985 /** 986 * Method to delete all instances of a tag from the mapping table. Generally used when a tag is deleted. 987 * 988 * @param integer $tagId The tag_id (primary key) for the deleted tag. 989 * 990 * @return void 991 * 992 * @since 3.1 993 */ 994 public function tagDeleteInstances($tagId) 995 { 996 // Cast as integer until method is typehinted. 997 $tag_id = (int) $tagId; 998 999 // Delete the old tag maps. 1000 $db = Factory::getDbo(); 1001 $query = $db->getQuery(true) 1002 ->delete($db->quoteName('#__contentitem_tag_map')) 1003 ->where($db->quoteName('tag_id') . ' = :id') 1004 ->bind(':id', $tagId, ParameterType::INTEGER); 1005 $db->setQuery($query); 1006 $db->execute(); 1007 } 1008 1009 /** 1010 * Method to add or update tags associated with an item. 1011 * 1012 * @param integer $ucmId Id of the #__ucm_content item being tagged 1013 * @param TableInterface $table Table object being tagged 1014 * @param array $tags Array of tags to be applied. 1015 * @param boolean $replace Flag indicating if all existing tags should be replaced 1016 * 1017 * @return boolean true on success, otherwise false. 1018 * 1019 * @since 3.1 1020 */ 1021 public function tagItem($ucmId, TableInterface $table, $tags = array(), $replace = true) 1022 { 1023 $key = $table->get('_tbl_key'); 1024 $oldTags = $this->getTagIds((int) $table->$key, $this->typeAlias); 1025 $oldTags = explode(',', $oldTags); 1026 $result = $this->unTagItem($ucmId, $table); 1027 1028 if ($replace) { 1029 $newTags = $tags; 1030 } else { 1031 if ($tags == array()) { 1032 $newTags = $table->newTags; 1033 } else { 1034 $newTags = $tags; 1035 } 1036 1037 if ($oldTags[0] != '') { 1038 $newTags = array_unique(array_merge($newTags, $oldTags)); 1039 } 1040 } 1041 1042 if (\is_array($newTags) && \count($newTags) > 0 && $newTags[0] != '') { 1043 $result = $result && $this->addTagMapping($ucmId, $table, $newTags); 1044 } 1045 1046 return $result; 1047 } 1048 1049 /** 1050 * Method to untag an item 1051 * 1052 * @param integer $contentId ID of the content item being untagged 1053 * @param TableInterface $table Table object being untagged 1054 * @param array $tags Array of tags to be untagged. Use an empty array to untag all existing tags. 1055 * 1056 * @return boolean true on success, otherwise false. 1057 * 1058 * @since 3.1 1059 */ 1060 public function unTagItem($contentId, TableInterface $table, $tags = array()) 1061 { 1062 $key = $table->getKeyName(); 1063 $id = (int) $table->$key; 1064 $db = Factory::getDbo(); 1065 $query = $db->getQuery(true) 1066 ->delete($db->quoteName('#__contentitem_tag_map')) 1067 ->where( 1068 [ 1069 $db->quoteName('type_alias') . ' = :type', 1070 $db->quoteName('content_item_id') . ' = :id', 1071 ] 1072 ) 1073 ->bind(':type', $this->typeAlias) 1074 ->bind(':id', $id, ParameterType::INTEGER); 1075 1076 if (\is_array($tags) && \count($tags) > 0) { 1077 $tags = ArrayHelper::toInteger($tags); 1078 1079 $query->whereIn($db->quoteName('tag_id'), $tags); 1080 } 1081 1082 $db->setQuery($query); 1083 1084 return (bool) $db->execute(); 1085 } 1086 }
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 |