PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/cms/helper/tags.php

https://github.com/J2MTecnologia/joomla-3.x
PHP | 1028 lines | 583 code | 148 blank | 297 comment | 97 complexity | 2c035ce18c898fb83137ff865912bbdd MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /**
  3. * @package Joomla.Libraries
  4. * @subpackage Helper
  5. *
  6. * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. /**
  11. * Tags helper class, provides methods to perform various tasks relevant
  12. * tagging of content.
  13. *
  14. * @package Joomla.Libraries
  15. * @subpackage Helper
  16. * @since 3.1
  17. */
  18. class JHelperTags extends JHelper
  19. {
  20. /**
  21. * Helper object for storing and deleting tag information.
  22. *
  23. * @var boolean
  24. * @since 3.1
  25. */
  26. protected $tagsChanged = false;
  27. /**
  28. * Whether up replace all tags or just add tags
  29. *
  30. * @var boolean
  31. * @since 3.1
  32. */
  33. protected $replaceTags = false;
  34. /**
  35. * Alias for querying mapping and content type table.
  36. *
  37. * @var string
  38. * @since 3.1
  39. */
  40. public $typeAlias = null;
  41. /**
  42. * Method to add tag rows to mapping table.
  43. *
  44. * @param integer $ucmId ID of the #__ucm_content item being tagged
  45. * @param JTableInterface $table JTable object being tagged
  46. * @param array $tags Array of tags to be applied.
  47. *
  48. * @return boolean true on success, otherwise false.
  49. *
  50. * @since 3.1
  51. */
  52. public function addTagMapping($ucmId, JTableInterface $table, $tags = array())
  53. {
  54. $db = $table->getDbo();
  55. $key = $table->getKeyName();
  56. $item = $table->$key;
  57. $typeId = $this->getTypeId($this->typeAlias);
  58. // Insert the new tag maps
  59. if (strpos('#', implode(',', $tags)) === false)
  60. {
  61. $tags = self::createTagsFromField($tags);
  62. }
  63. $query = $db->getQuery(true);
  64. $query->insert('#__contentitem_tag_map');
  65. $query->columns(array($db->quoteName('type_alias'), $db->quoteName('core_content_id'), $db->quoteName('content_item_id'), $db->quoteName('tag_id'), $db->quoteName('tag_date'), $db->quoteName('type_id')));
  66. foreach ($tags as $tag)
  67. {
  68. $query->values($db->quote($this->typeAlias) . ', ' . (int) $ucmId . ', ' . (int) $item . ', ' . $db->quote($tag) . ', ' . $query->currentTimestamp() . ', ' . (int) $typeId);
  69. }
  70. $db->setQuery($query);
  71. return (boolean) $db->execute();
  72. }
  73. /**
  74. * Function that converts tags paths into paths of names
  75. *
  76. * @param array $tags Array of tags
  77. *
  78. * @return array
  79. *
  80. * @since 3.1
  81. */
  82. public static function convertPathsToNames($tags)
  83. {
  84. // We will replace path aliases with tag names
  85. if ($tags)
  86. {
  87. // Create an array with all the aliases of the results
  88. $aliases = array();
  89. foreach ($tags as $tag)
  90. {
  91. if (!empty($tag->path))
  92. {
  93. if ($pathParts = explode('/', $tag->path))
  94. {
  95. $aliases = array_merge($aliases, $pathParts);
  96. }
  97. }
  98. }
  99. // Get the aliases titles in one single query and map the results
  100. if ($aliases)
  101. {
  102. // Remove duplicates
  103. $aliases = array_unique($aliases);
  104. $db = JFactory::getDbo();
  105. $query = $db->getQuery(true)
  106. ->select('alias, title')
  107. ->from('#__tags')
  108. ->where('alias IN (' . implode(',', array_map(array($db, 'quote'), $aliases)) . ')');
  109. $db->setQuery($query);
  110. try
  111. {
  112. $aliasesMapper = $db->loadAssocList('alias');
  113. }
  114. catch (RuntimeException $e)
  115. {
  116. return false;
  117. }
  118. // Rebuild the items path
  119. if ($aliasesMapper)
  120. {
  121. foreach ($tags as $tag)
  122. {
  123. $namesPath = array();
  124. if (!empty($tag->path))
  125. {
  126. if ($pathParts = explode('/', $tag->path))
  127. {
  128. foreach ($pathParts as $alias)
  129. {
  130. if (isset($aliasesMapper[$alias]))
  131. {
  132. $namesPath[] = $aliasesMapper[$alias]['title'];
  133. }
  134. else
  135. {
  136. $namesPath[] = $alias;
  137. }
  138. }
  139. $tag->text = implode('/', $namesPath);
  140. }
  141. }
  142. }
  143. }
  144. }
  145. }
  146. return $tags;
  147. }
  148. /**
  149. * Create any new tags by looking for #new# in the strings
  150. *
  151. * @param array $tags Tags text array from the field
  152. *
  153. * @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false.
  154. *
  155. * @since 3.1
  156. */
  157. public function createTagsFromField($tags)
  158. {
  159. if (empty($tags) || $tags[0] == '')
  160. {
  161. return;
  162. }
  163. else
  164. {
  165. // We will use the tags table to store them
  166. JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tags/tables');
  167. $tagTable = JTable::getInstance('Tag', 'TagsTable');
  168. $newTags = array();
  169. foreach ($tags as $key => $tag)
  170. {
  171. // Remove the #new# prefix that identifies new tags
  172. $tagText = str_replace('#new#', '', $tag);
  173. if ($tagText == $tag)
  174. {
  175. $newTags[] = (int) $tag;
  176. }
  177. else
  178. {
  179. // Clear old data if exist
  180. $tagTable->reset();
  181. // Try to load the selected tag
  182. if ($tagTable->load(array('title' => $tagText)))
  183. {
  184. $newTags[] = (int) $tagTable->id;
  185. }
  186. else
  187. {
  188. // Prepare tag data
  189. $tagTable->id = 0;
  190. $tagTable->title = $tagText;
  191. $tagTable->published = 1;
  192. // $tagTable->language = property_exists ($item, 'language') ? $item->language : '*';
  193. $tagTable->language = '*';
  194. $tagTable->access = 1;
  195. // Make this item a child of the root tag
  196. $tagTable->setLocation($tagTable->getRootId(), 'last-child');
  197. // Try to store tag
  198. if ($tagTable->check())
  199. {
  200. // Assign the alias as path (autogenerated tags have always level 1)
  201. $tagTable->path = $tagTable->alias;
  202. if ($tagTable->store())
  203. {
  204. $newTags[] = (int) $tagTable->id;
  205. }
  206. }
  207. }
  208. }
  209. }
  210. // At this point $tags is an array of all tag ids
  211. $this->tags = $newTags;
  212. $result = $newTags;
  213. }
  214. return $result;
  215. }
  216. /**
  217. * Create any new tags by looking for #new# in the metadata
  218. *
  219. * @param string $metadata Metadata JSON string
  220. *
  221. * @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false.
  222. *
  223. * @since 3.1
  224. * @deprecated 4.0 This method is no longer used in the CMS and will not be replaced.
  225. */
  226. public function createTagsFromMetadata($metadata)
  227. {
  228. $metaObject = json_decode($metadata);
  229. if (empty($metaObject->tags))
  230. {
  231. return $metadata;
  232. }
  233. $tags = $metaObject->tags;
  234. if (empty($tags) || !is_array($tags))
  235. {
  236. $result = $metadata;
  237. }
  238. else
  239. {
  240. // We will use the tags table to store them
  241. JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tags/tables');
  242. $tagTable = JTable::getInstance('Tag', 'TagsTable');
  243. $newTags = array();
  244. foreach ($tags as $tag)
  245. {
  246. // Remove the #new# prefix that identifies new tags
  247. $tagText = str_replace('#new#', '', $tag);
  248. if ($tagText == $tag)
  249. {
  250. $newTags[] = (int) $tag;
  251. }
  252. else
  253. {
  254. // Clear old data if exist
  255. $tagTable->reset();
  256. // Try to load the selected tag
  257. if ($tagTable->load(array('title' => $tagText)))
  258. {
  259. $newTags[] = (int) $tagTable->id;
  260. }
  261. else
  262. {
  263. // Prepare tag data
  264. $tagTable->id = 0;
  265. $tagTable->title = $tagText;
  266. $tagTable->published = 1;
  267. // $tagTable->language = property_exists ($item, 'language') ? $item->language : '*';
  268. $tagTable->language = '*';
  269. $tagTable->access = 1;
  270. // Make this item a child of the root tag
  271. $tagTable->setLocation($tagTable->getRootId(), 'last-child');
  272. // Try to store tag
  273. if ($tagTable->check())
  274. {
  275. // Assign the alias as path (autogenerated tags have always level 1)
  276. $tagTable->path = $tagTable->alias;
  277. if ($tagTable->store())
  278. {
  279. $newTags[] = (int) $tagTable->id;
  280. }
  281. }
  282. }
  283. }
  284. }
  285. // At this point $tags is an array of all tag ids
  286. $metaObject->tags = $newTags;
  287. $result = json_encode($metaObject);
  288. }
  289. return $result;
  290. }
  291. /**
  292. * Method to delete the tag mappings and #__ucm_content record for for an item
  293. *
  294. * @param JTableInterface $table JTable object of content table where delete occurred
  295. * @param integer $contentItemId ID of the content item.
  296. *
  297. * @return boolean true on success, false on failure
  298. *
  299. * @since 3.1
  300. */
  301. public function deleteTagData(JTableInterface $table, $contentItemId)
  302. {
  303. $result = $this->unTagItem($contentItemId, $table);
  304. /**
  305. * @var JTableCorecontent $ucmContentTable
  306. */
  307. $ucmContentTable = JTable::getInstance('Corecontent');
  308. return $result && $ucmContentTable->deleteByContentId($contentItemId);
  309. }
  310. /**
  311. * Method to get a list of tags for an item, optionally with the tag data.
  312. *
  313. * @param integer $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. // Initialize some variables.
  324. $db = JFactory::getDbo();
  325. $query = $db->getQuery(true)
  326. ->select($db->quoteName('m.tag_id'))
  327. ->from($db->quoteName('#__contentitem_tag_map') . ' AS m ')
  328. ->where(
  329. array(
  330. $db->quoteName('m.type_alias') . ' = ' . $db->quote($contentType),
  331. $db->quoteName('m.content_item_id') . ' = ' . (int) $id,
  332. $db->quoteName('t.published') . ' = 1'
  333. )
  334. );
  335. $user = JFactory::getUser();
  336. $groups = implode(',', $user->getAuthorisedViewLevels());
  337. $query->where('t.access IN (' . $groups . ')');
  338. // Optionally filter on language
  339. $language = JComponentHelper::getParams('com_tags')->get('tag_list_language_filter', 'all');
  340. if ($language != 'all')
  341. {
  342. if ($language == 'current_language')
  343. {
  344. $language = $this->getCurrentLanguage();
  345. }
  346. $query->where($db->quoteName('language') . ' IN (' . $db->quote($language) . ', ' . $db->quote('*') . ')');
  347. }
  348. if ($getTagData)
  349. {
  350. $query->select($db->quoteName('t') . '.*');
  351. }
  352. $query->join('INNER', $db->quoteName('#__tags') . ' AS t ' . ' ON ' . $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id'));
  353. $db->setQuery($query);
  354. $this->itemTags = $db->loadObjectList();
  355. return $this->itemTags;
  356. }
  357. /**
  358. * Method to get a list of tags for a given item.
  359. * Normally used for displaying a list of tags within a layout
  360. *
  361. * @param mixed $ids The id or array of ids (primary key) of the item to be tagged.
  362. * @param string $prefix Dot separated string with the option and view to be used for a url.
  363. *
  364. * @return string Comma separated list of tag Ids.
  365. *
  366. * @since 3.1
  367. */
  368. public function getTagIds($ids, $prefix)
  369. {
  370. if (empty($ids))
  371. {
  372. return;
  373. }
  374. /**
  375. * Ids possible formats:
  376. * ---------------------
  377. * $id = 1;
  378. * $id = array(1,2);
  379. * $id = array('1,3,4,19');
  380. * $id = '1,3';
  381. */
  382. $ids = (array) $ids;
  383. $ids = implode(',', $ids);
  384. $ids = explode(',', $ids);
  385. JArrayHelper::toInteger($ids);
  386. $db = JFactory::getDbo();
  387. // Load the tags.
  388. $query = $db->getQuery(true)
  389. ->select($db->quoteName('t.id'))
  390. ->from($db->quoteName('#__tags') . ' AS t ')
  391. ->join(
  392. 'INNER', $db->quoteName('#__contentitem_tag_map') . ' AS m'
  393. . ' ON ' . $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id')
  394. . ' AND ' . $db->quoteName('m.type_alias') . ' = ' . $db->quote($prefix)
  395. . ' AND ' . $db->quoteName('m.content_item_id') . ' IN ( ' . implode(',', $ids) . ')'
  396. );
  397. $db->setQuery($query);
  398. // Add the tags to the content data.
  399. $tagsList = $db->loadColumn();
  400. $this->tags = implode(',', $tagsList);
  401. return $this->tags;
  402. }
  403. /**
  404. * Method to get a query to retrieve a detailed list of items for a tag.
  405. *
  406. * @param mixed $tagId Tag or array of tags to be matched
  407. * @param mixed $typesr Null, type or array of type aliases for content types to be included in the results
  408. * @param boolean $includeChildren True to include the results from child tags
  409. * @param string $orderByOption Column to order the results by
  410. * @param string $orderDir Direction to sort the results in
  411. * @param boolean $anyOrAll True to include items matching at least one tag, false to include
  412. * items all tags in the array.
  413. * @param string $languageFilter Optional filter on language. Options are 'all', 'current' or any string.
  414. * @param string $stateFilter Optional filtering on publication state, defaults to published or unpublished.
  415. *
  416. * @return JDatabaseQuery Query to retrieve a list of tags
  417. *
  418. * @since 3.1
  419. */
  420. public function getTagItemsQuery($tagId, $typesr = null, $includeChildren = false, $orderByOption = 'c.core_title', $orderDir = 'ASC',
  421. $anyOrAll = true, $languageFilter = 'all', $stateFilter = '0,1')
  422. {
  423. // Create a new query object.
  424. $db = JFactory::getDbo();
  425. $query = $db->getQuery(true);
  426. $user = JFactory::getUser();
  427. $nullDate = $db->quote($db->getNullDate());
  428. $nowDate = $db->quote(JFactory::getDate()->toSql());
  429. $ntagsr = substr_count($tagId, ',') + 1;
  430. // Force ids to array and sanitize
  431. $tagIds = (array) $tagId;
  432. $tagIds = implode(',', $tagIds);
  433. $tagIds = explode(',', $tagIds);
  434. JArrayHelper::toInteger($tagIds);
  435. // If we want to include children we have to adjust the list of tags.
  436. // We do not search child tags when the match all option is selected.
  437. if ($includeChildren)
  438. {
  439. $tagTreeList = '';
  440. $tagTreeArray = array();
  441. foreach ($tagIds as $tag)
  442. {
  443. $this->getTagTreeArray($tag, $tagTreeArray);
  444. }
  445. $tagIds = array_unique(array_merge($tagIds, $tagTreeArray));
  446. }
  447. // Sanitize filter states
  448. $stateFilters = explode(',', $stateFilter);
  449. JArrayHelper::toInteger($stateFilters);
  450. // M is the mapping table. C is the core_content table. Ct is the content_types table.
  451. $query->select('m.type_alias, m.content_item_id, m.core_content_id, count(m.tag_id) AS match_count, MAX(m.tag_date) as tag_date, MAX(c.core_title) AS core_title')
  452. ->select('MAX(c.core_alias) AS core_alias, MAX(c.core_body) AS core_body, MAX(c.core_state) AS core_state, MAX(c.core_access) AS core_access')
  453. ->select('MAX(c.core_metadata) AS core_metadata, MAX(c.core_created_user_id) AS core_created_user_id, MAX(c.core_created_by_alias) AS core_created_by_alias')
  454. ->select('MAX(c.core_created_time) as core_created_time, MAX(c.core_images) as core_images')
  455. ->select('CASE WHEN c.core_modified_time = ' . $nullDate . ' THEN c.core_created_time ELSE c.core_modified_time END as core_modified_time')
  456. ->select('MAX(c.core_language) AS core_language, MAX(c.core_catid) AS core_catid')
  457. ->select('MAX(c.core_publish_up) AS core_publish_up, MAX(c.core_publish_down) as core_publish_down')
  458. ->select('MAX(ct.type_title) AS content_type_title, MAX(ct.router) AS router')
  459. ->from('#__contentitem_tag_map AS m')
  460. ->join('INNER', '#__ucm_content AS c ON m.type_alias = c.core_type_alias AND m.core_content_id = c.core_content_id AND c.core_state IN (' . implode(',', $stateFilters) . ')' . (in_array('0', $stateFilters) ? '' : ' AND (c.core_publish_up = ' . $nullDate . ' OR c.core_publish_up <= ' . $nowDate . ') AND (c.core_publish_down = ' . $nullDate . ' OR c.core_publish_down >= ' . $nowDate . ')'))
  461. ->join('INNER', '#__content_types AS ct ON ct.type_alias = m.type_alias')
  462. // Join over the users for the author and email
  463. ->select("CASE WHEN c.core_created_by_alias > ' ' THEN c.core_created_by_alias ELSE ua.name END AS author")
  464. ->select("ua.email AS author_email")
  465. ->join('LEFT', '#__users AS ua ON ua.id = c.core_created_user_id')
  466. ->where('m.tag_id IN (' . implode(',', $tagIds) . ')');
  467. // Optionally filter on language
  468. if (empty($language))
  469. {
  470. $language = $languageFilter;
  471. }
  472. if ($language != 'all')
  473. {
  474. if ($language == 'current_language')
  475. {
  476. $language = $this->getCurrentLanguage();
  477. }
  478. $query->where($db->quoteName('c.core_language') . ' IN (' . $db->quote($language) . ', ' . $db->quote('*') . ')');
  479. }
  480. // Get the type data, limited to types in the request if there are any specified.
  481. $typesarray = self::getTypes('assocList', $typesr, false);
  482. $typeAliases = '';
  483. foreach ($typesarray as $type)
  484. {
  485. $typeAliases .= "'" . $type['type_alias'] . "'" . ',';
  486. }
  487. $typeAliases = rtrim($typeAliases, ',');
  488. $query->where('m.type_alias IN (' . $typeAliases . ')');
  489. $groups = '0,' . implode(',', array_unique($user->getAuthorisedViewLevels()));
  490. $query->where('c.core_access IN (' . $groups . ')')
  491. ->group('m.type_alias, m.content_item_id, m.core_content_id');
  492. // Use HAVING if matching all tags and we are matching more than one tag.
  493. if ($ntagsr > 1 && $anyOrAll != 1 && $includeChildren != 1)
  494. {
  495. // The number of results should equal the number of tags requested.
  496. $query->having("COUNT('m.tag_id') = " . (int) $ntagsr);
  497. }
  498. // Set up the order by using the option chosen
  499. if ($orderByOption == 'match_count')
  500. {
  501. $orderBy = 'COUNT(m.tag_id)';
  502. }
  503. else
  504. {
  505. $orderBy = 'MAX(' . $db->quoteName($orderByOption) . ')';
  506. }
  507. $query->order($orderBy . ' ' . $orderDir);
  508. return $query;
  509. }
  510. /**
  511. * Function that converts tag ids to their tag names
  512. *
  513. * @param array $tagIds Array of integer tag ids.
  514. *
  515. * @return array An array of tag names.
  516. *
  517. * @since 3.1
  518. */
  519. public function getTagNames($tagIds)
  520. {
  521. $tagNames = array();
  522. if (is_array($tagIds) && count($tagIds) > 0)
  523. {
  524. JArrayHelper::toInteger($tagIds);
  525. $db = JFactory::getDbo();
  526. $query = $db->getQuery(true)
  527. ->select($db->quoteName('title'))
  528. ->from($db->quoteName('#__tags'))
  529. ->where($db->quoteName('id') . ' IN (' . implode(',', $tagIds) . ')');
  530. $query->order($db->quoteName('title'));
  531. $db->setQuery($query);
  532. $tagNames = $db->loadColumn();
  533. }
  534. return $tagNames;
  535. }
  536. /**
  537. * Method to get an array of tag ids for the current tag and its children
  538. *
  539. * @param integer $id An optional ID
  540. * @param array &$tagTreeArray Array containing the tag tree
  541. *
  542. * @return mixed
  543. *
  544. * @since 3.1
  545. */
  546. public function getTagTreeArray($id, &$tagTreeArray = array())
  547. {
  548. // Get a level row instance.
  549. $table = JTable::getInstance('Tag', 'TagsTable');
  550. if ($table->isLeaf($id))
  551. {
  552. $tagTreeArray[] = $id;
  553. return $tagTreeArray;
  554. }
  555. $tagTree = $table->getTree($id);
  556. // Attempt to load the tree
  557. if ($tagTree)
  558. {
  559. foreach ($tagTree as $tag)
  560. {
  561. $tagTreeArray[] = $tag->id;
  562. }
  563. return $tagTreeArray;
  564. }
  565. }
  566. /**
  567. * Method to get the type id for a type alias.
  568. *
  569. * @param string $typeAlias A type alias.
  570. *
  571. * @return string Name of the table for a type
  572. *
  573. * @since 3.1
  574. * @deprecated 4.0 Use JUcmType::getTypeId() instead
  575. */
  576. public function getTypeId($typeAlias)
  577. {
  578. $contentType = new JUcmType;
  579. return $contentType->getTypeId($typeAlias);
  580. }
  581. /**
  582. * Method to get a list of types with associated data.
  583. *
  584. * @param string $arrayType Optionally specify that the returned list consist of objects, associative arrays, or arrays.
  585. * Options are: rowList, assocList, and objectList
  586. * @param array $selectTypes Optional array of type ids to limit the results to. Often from a request.
  587. * @param boolean $useAlias If true, the alias is used to match, if false the type_id is used.
  588. *
  589. * @return array Array of of types
  590. *
  591. * @since 3.1
  592. */
  593. public static function getTypes($arrayType = 'objectList', $selectTypes = null, $useAlias = true)
  594. {
  595. // Initialize some variables.
  596. $db = JFactory::getDbo();
  597. $query = $db->getQuery(true)
  598. ->select('*');
  599. if (!empty($selectTypes))
  600. {
  601. $selectTypes = (array) $selectTypes;
  602. if ($useAlias)
  603. {
  604. $selectTypes = array_map(array($db, 'quote'), $selectTypes);
  605. $query->where($db->quoteName('type_alias') . ' IN (' . implode(',', $selectTypes) . ')');
  606. }
  607. else
  608. {
  609. JArrayHelper::toInteger($selectTypes);
  610. $query->where($db->quoteName('type_id') . ' IN (' . implode(',', $selectTypes) . ')');
  611. }
  612. }
  613. $query->from($db->quoteName('#__content_types'));
  614. $db->setQuery($query);
  615. switch ($arrayType)
  616. {
  617. case 'assocList':
  618. $types = $db->loadAssocList();
  619. break;
  620. case 'rowList':
  621. $types = $db->loadRowList();
  622. break;
  623. case 'objectList':
  624. default:
  625. $types = $db->loadObjectList();
  626. break;
  627. }
  628. return $types;
  629. }
  630. /**
  631. * Function that handles saving tags used in a table class after a store()
  632. *
  633. * @param JTableInterface $table JTable being processed
  634. * @param array $newTags Array of new tags
  635. * @param boolean $replace Flag indicating if all exising tags should be replaced
  636. *
  637. * @return boolean
  638. *
  639. * @since 3.1
  640. */
  641. public function postStoreProcess(JTableInterface $table, $newTags = array(), $replace = true)
  642. {
  643. if (!empty($table->newTags) && empty($newTags))
  644. {
  645. $newTags = $table->newTags;
  646. }
  647. // If existing row, check to see if tags have changed.
  648. $newTable = clone $table;
  649. $newTable->reset();
  650. $key = $newTable->getKeyName();
  651. $typeAlias = $this->typeAlias;
  652. $result = true;
  653. // Process ucm_content and ucm_base if either tags have changed or we have some tags.
  654. if ($this->tagsChanged || (!empty($newTags) && $newTags[0] != ''))
  655. {
  656. if (!$newTags && $replace = true)
  657. {
  658. // Delete all tags data
  659. $key = $table->getKeyName();
  660. $result = $this->deleteTagData($table, $table->$key);
  661. }
  662. else
  663. {
  664. // Process the tags
  665. $data = $this->getRowData($table);
  666. $ucmContentTable = JTable::getInstance('Corecontent');
  667. $ucm = new JUcmContent($table, $this->typeAlias);
  668. $ucmData = $data ? $ucm->mapData($data) : $ucm->ucmData;
  669. $primaryId = $ucm->getPrimaryKey($ucmData['common']['core_type_id'], $ucmData['common']['core_content_item_id']);
  670. $result = $ucmContentTable->load($primaryId);
  671. $result = $result && $ucmContentTable->bind($ucmData['common']);
  672. $result = $result && $ucmContentTable->check();
  673. $result = $result && $ucmContentTable->store();
  674. $ucmId = $ucmContentTable->core_content_id;
  675. // Store the tag data if the article data was saved and run related methods.
  676. $result = $result && $this->tagItem($ucmId, $table, $newTags, $replace);
  677. }
  678. }
  679. return $result;
  680. }
  681. /**
  682. * Function that preProcesses data from a table prior to a store() to ensure proper tag handling
  683. *
  684. * @param JTableInterface $table JTable being processed
  685. * @param array $newTags Array of new tags
  686. *
  687. * @return null
  688. *
  689. * @since 3.1
  690. */
  691. public function preStoreProcess(JTableInterface $table, $newTags = array())
  692. {
  693. if ($newTags != array())
  694. {
  695. $this->newTags = $newTags;
  696. }
  697. // If existing row, check to see if tags have changed.
  698. $oldTable = clone $table;
  699. $oldTable->reset();
  700. $key = $oldTable->getKeyName();
  701. $typeAlias = $this->typeAlias;
  702. if ($oldTable->$key && $oldTable->load())
  703. {
  704. $this->oldTags = $this->getTagIds($oldTable->$key, $typeAlias);
  705. }
  706. // New items with no tags bypass this step.
  707. if ((!empty($newTags) && is_string($newTags) || (isset($newTags[0]) && $newTags[0] != '')) || isset($this->oldTags))
  708. {
  709. if (is_array($newTags))
  710. {
  711. $newTags = implode(',', $newTags);
  712. }
  713. // We need to process tags if the tags have changed or if we have a new row
  714. $this->tagsChanged = (empty($this->oldTags) && !empty($newTags)) ||(!empty($this->oldTags) && $this->oldTags != $newTags) || !$table->$key;
  715. }
  716. }
  717. /**
  718. * Function to search tags
  719. *
  720. * @param array $filters Filter to apply to the search
  721. *
  722. * @return array
  723. *
  724. * @since 3.1
  725. */
  726. public static function searchTags($filters = array())
  727. {
  728. $db = JFactory::getDbo();
  729. $query = $db->getQuery(true)
  730. ->select('a.id AS value')
  731. ->select('a.path AS text')
  732. ->select('a.path')
  733. ->from('#__tags AS a')
  734. ->join('LEFT', $db->quoteName('#__tags', 'b') . ' ON a.lft > b.lft AND a.rgt < b.rgt');
  735. // Filter language
  736. if (!empty($filters['flanguage']))
  737. {
  738. $query->where('a.language IN (' . $db->quote($filters['flanguage']) . ',' . $db->quote('*') . ') ');
  739. }
  740. // Do not return root
  741. $query->where($db->quoteName('a.alias') . ' <> ' . $db->quote('root'));
  742. // Search in title or path
  743. if (!empty($filters['like']))
  744. {
  745. $query->where(
  746. '(' . $db->quoteName('a.title') . ' LIKE ' . $db->quote('%' . $filters['like'] . '%')
  747. . ' OR ' . $db->quoteName('a.path') . ' LIKE ' . $db->quote('%' . $filters['like'] . '%') . ')'
  748. );
  749. }
  750. // Filter title
  751. if (!empty($filters['title']))
  752. {
  753. $query->where($db->quoteName('a.title') . ' = ' . $db->quote($filters['title']));
  754. }
  755. // Filter on the published state
  756. if (is_numeric($filters['published']))
  757. {
  758. $query->where('a.published = ' . (int) $filters['published']);
  759. }
  760. // Filter by parent_id
  761. if (!empty($filters['parent_id']))
  762. {
  763. JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tags/tables');
  764. $tagTable = JTable::getInstance('Tag', 'TagsTable');
  765. if ($children = $tagTable->getTree($filters['parent_id']))
  766. {
  767. foreach ($children as $child)
  768. {
  769. $childrenIds[] = $child->id;
  770. }
  771. $query->where('a.id IN (' . implode(',', $childrenIds) . ')');
  772. }
  773. }
  774. $query->group('a.id, a.title, a.level, a.lft, a.rgt, a.parent_id, a.published, a.path')
  775. ->order('a.lft ASC');
  776. // Get the options.
  777. $db->setQuery($query);
  778. try
  779. {
  780. $results = $db->loadObjectList();
  781. }
  782. catch (RuntimeException $e)
  783. {
  784. return false;
  785. }
  786. // We will replace path aliases with tag names
  787. $results = self::convertPathsToNames($results);
  788. return $results;
  789. }
  790. /**
  791. * Method to delete all instances of a tag from the mapping table. Generally used when a tag is deleted.
  792. *
  793. * @param integer $tag_id The tag_id (primary key) for the deleted tag.
  794. *
  795. * @return void
  796. *
  797. * @since 3.1
  798. */
  799. public function tagDeleteInstances($tag_id)
  800. {
  801. // Delete the old tag maps.
  802. $db = JFactory::getDbo();
  803. $query = $db->getQuery(true)
  804. ->delete($db->quoteName('#__contentitem_tag_map'))
  805. ->where($db->quoteName('tag_id') . ' = ' . (int) $tag_id);
  806. $db->setQuery($query);
  807. $db->execute();
  808. }
  809. /**
  810. * Method to add or update tags associated with an item.
  811. *
  812. * @param integer $ucmId Id of the #__ucm_content item being tagged
  813. * @param JTableInterface $table JTable object being tagged
  814. * @param array $tags Array of tags to be applied.
  815. * @param boolean $replace Flag indicating if all exising tags should be replaced
  816. *
  817. * @return boolean true on success, otherwise false.
  818. *
  819. * @since 3.1
  820. */
  821. public function tagItem($ucmId, JTableInterface $table, $tags = array(), $replace = true)
  822. {
  823. $key = $table->get('_tbl_key');
  824. $oldTags = $this->getTagIds((int) $table->$key, $this->typeAlias);
  825. $oldTags = explode(',', $oldTags);
  826. $result = $this->unTagItem($ucmId, $table);
  827. if ($replace)
  828. {
  829. $newTags = $tags;
  830. }
  831. else
  832. {
  833. if ($tags == array())
  834. {
  835. $newTags = $table->newTags;
  836. }
  837. else
  838. {
  839. $newTags = $tags;
  840. }
  841. if ($oldTags[0] != '')
  842. {
  843. $newTags = array_unique(array_merge($newTags, $oldTags));
  844. }
  845. }
  846. if (is_array($newTags) && count($newTags) > 0 && $newTags[0] != '')
  847. {
  848. $result = $result && $this->addTagMapping($ucmId, $table, $newTags);
  849. }
  850. return $result;
  851. }
  852. /**
  853. * Method to untag an item
  854. *
  855. * @param integer $contentId ID of the content item being untagged
  856. * @param JTableInterface $table JTable object being untagged
  857. * @param array $tags Array of tags to be untagged. Use an empty array to untag all existing tags.
  858. *
  859. * @return boolean true on success, otherwise false.
  860. *
  861. * @since 3.1
  862. */
  863. public function unTagItem($contentId, JTableInterface $table, $tags = array())
  864. {
  865. $key = $table->getKeyName();
  866. $id = $table->$key;
  867. $db = JFactory::getDbo();
  868. $query = $db->getQuery(true)
  869. ->delete('#__contentitem_tag_map')
  870. ->where($db->quoteName('type_alias') . ' = ' . $db->quote($this->typeAlias))
  871. ->where($db->quoteName('content_item_id') . ' = ' . (int) $id);
  872. if (is_array($tags) && count($tags) > 0)
  873. {
  874. JArrayHelper::toInteger($tags);
  875. $query->where($db->quoteName('tag_id') . ' IN ' . implode(',', $tags));
  876. }
  877. $db->setQuery($query);
  878. return (boolean) $db->execute();
  879. }
  880. }