[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Helper/ -> TagsHelper.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer