1<?php
2/**
3 * @package     Joomla.Administrator
4 * @subpackage  com_finder
5 *
6 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
7 * @license     GNU General Public License version 2 or later; see LICENSE.txt
8 */
9
10defined('_JEXEC') or die;
11
12/**
13 * Stemmer base class for the Finder indexer package.
14 *
15 * @since  2.5
16 */
17class FinderIndexerTaxonomy
18{
19	/**
20	 * An internal cache of taxonomy branch data.
21	 *
22	 * @var    array
23	 * @since  2.5
24	 */
25	public static $branches = array();
26
27	/**
28	 * An internal cache of taxonomy node data.
29	 *
30	 * @var    array
31	 * @since  2.5
32	 */
33	public static $nodes = array();
34
35	/**
36	 * Method to add a branch to the taxonomy tree.
37	 *
38	 * @param   string   $title   The title of the branch.
39	 * @param   integer  $state   The published state of the branch. [optional]
40	 * @param   integer  $access  The access state of the branch. [optional]
41	 *
42	 * @return  integer  The id of the branch.
43	 *
44	 * @since   2.5
45	 * @throws  Exception on database error.
46	 */
47	public static function addBranch($title, $state = 1, $access = 1)
48	{
49		// Check to see if the branch is in the cache.
50		if (isset(static::$branches[$title]))
51		{
52			return static::$branches[$title]->id;
53		}
54
55		// Check to see if the branch is in the table.
56		$db = JFactory::getDbo();
57		$query = $db->getQuery(true)
58			->select('*')
59			->from($db->quoteName('#__finder_taxonomy'))
60			->where($db->quoteName('parent_id') . ' = 1')
61			->where($db->quoteName('title') . ' = ' . $db->quote($title));
62		$db->setQuery($query);
63
64		// Get the result.
65		$result = $db->loadObject();
66
67		// Check if the database matches the input data.
68		if ((bool) $result && $result->state == $state && $result->access == $access)
69		{
70			// The data matches, add the item to the cache.
71			static::$branches[$title] = $result;
72
73			return static::$branches[$title]->id;
74		}
75
76		/*
77		 * The database did not match the input. This could be because the
78		 * state has changed or because the branch does not exist. Let's figure
79		 * out which case is true and deal with it.
80		 */
81		$branch = new JObject;
82
83		if (empty($result))
84		{
85			// Prepare the branch object.
86			$branch->parent_id = 1;
87			$branch->title = $title;
88			$branch->state = (int) $state;
89			$branch->access = (int) $access;
90		}
91		else
92		{
93			// Prepare the branch object.
94			$branch->id = (int) $result->id;
95			$branch->parent_id = (int) $result->parent_id;
96			$branch->title = $result->title;
97			$branch->state = (int) $result->title;
98			$branch->access = (int) $result->access;
99			$branch->ordering = (int) $result->ordering;
100		}
101
102		// Store the branch.
103		static::storeNode($branch);
104
105		// Add the branch to the cache.
106		static::$branches[$title] = $branch;
107
108		return static::$branches[$title]->id;
109	}
110
111	/**
112	 * Method to add a node to the taxonomy tree.
113	 *
114	 * @param   string   $branch  The title of the branch to store the node in.
115	 * @param   string   $title   The title of the node.
116	 * @param   integer  $state   The published state of the node. [optional]
117	 * @param   integer  $access  The access state of the node. [optional]
118	 *
119	 * @return  integer  The id of the node.
120	 *
121	 * @since   2.5
122	 * @throws  Exception on database error.
123	 */
124	public static function addNode($branch, $title, $state = 1, $access = 1)
125	{
126		// Check to see if the node is in the cache.
127		if (isset(static::$nodes[$branch][$title]))
128		{
129			return static::$nodes[$branch][$title]->id;
130		}
131
132		// Get the branch id, insert it if it does not exist.
133		$branchId = static::addBranch($branch);
134
135		// Check to see if the node is in the table.
136		$db = JFactory::getDbo();
137		$query = $db->getQuery(true)
138			->select('*')
139			->from($db->quoteName('#__finder_taxonomy'))
140			->where($db->quoteName('parent_id') . ' = ' . $db->quote($branchId))
141			->where($db->quoteName('title') . ' = ' . $db->quote($title));
142		$db->setQuery($query);
143
144		// Get the result.
145		$result = $db->loadObject();
146
147		// Check if the database matches the input data.
148		if ((bool) $result && $result->state == $state && $result->access == $access)
149		{
150			// The data matches, add the item to the cache.
151			static::$nodes[$branch][$title] = $result;
152
153			return static::$nodes[$branch][$title]->id;
154		}
155
156		/*
157		 * The database did not match the input. This could be because the
158		 * state has changed or because the node does not exist. Let's figure
159		 * out which case is true and deal with it.
160		 */
161		$node = new JObject;
162
163		if (empty($result))
164		{
165			// Prepare the node object.
166			$node->parent_id = (int) $branchId;
167			$node->title = $title;
168			$node->state = (int) $state;
169			$node->access = (int) $access;
170		}
171		else
172		{
173			// Prepare the node object.
174			$node->id = (int) $result->id;
175			$node->parent_id = (int) $result->parent_id;
176			$node->title = $result->title;
177			$node->state = (int) $result->title;
178			$node->access = (int) $result->access;
179			$node->ordering = (int) $result->ordering;
180		}
181
182		// Store the node.
183		static::storeNode($node);
184
185		// Add the node to the cache.
186		static::$nodes[$branch][$title] = $node;
187
188		return static::$nodes[$branch][$title]->id;
189	}
190
191	/**
192	 * Method to add a map entry between a link and a taxonomy node.
193	 *
194	 * @param   integer  $linkId  The link to map to.
195	 * @param   integer  $nodeId  The node to map to.
196	 *
197	 * @return  boolean  True on success.
198	 *
199	 * @since   2.5
200	 * @throws  Exception on database error.
201	 */
202	public static function addMap($linkId, $nodeId)
203	{
204		// Insert the map.
205		$db = JFactory::getDbo();
206
207		$query = $db->getQuery(true)
208			->select($db->quoteName('link_id'))
209			->from($db->quoteName('#__finder_taxonomy_map'))
210			->where($db->quoteName('link_id') . ' = ' . (int) $linkId)
211			->where($db->quoteName('node_id') . ' = ' . (int) $nodeId);
212		$db->setQuery($query);
213		$db->execute();
214		$id = (int) $db->loadResult();
215
216		$map = new JObject;
217		$map->link_id = (int) $linkId;
218		$map->node_id = (int) $nodeId;
219
220		if ($id)
221		{
222			$db->updateObject('#__finder_taxonomy_map', $map, array('link_id', 'node_id'));
223		}
224		else
225		{
226			$db->insertObject('#__finder_taxonomy_map', $map);
227		}
228
229		return true;
230	}
231
232	/**
233	 * Method to get the title of all taxonomy branches.
234	 *
235	 * @return  array  An array of branch titles.
236	 *
237	 * @since   2.5
238	 * @throws  Exception on database error.
239	 */
240	public static function getBranchTitles()
241	{
242		$db = JFactory::getDbo();
243
244		// Set user variables
245		$groups = implode(',', JFactory::getUser()->getAuthorisedViewLevels());
246
247		// Create a query to get the taxonomy branch titles.
248		$query = $db->getQuery(true)
249			->select($db->quoteName('title'))
250			->from($db->quoteName('#__finder_taxonomy'))
251			->where($db->quoteName('parent_id') . ' = 1')
252			->where($db->quoteName('state') . ' = 1')
253			->where($db->quoteName('access') . ' IN (' . $groups . ')');
254
255		// Get the branch titles.
256		$db->setQuery($query);
257
258		return $db->loadColumn();
259	}
260
261	/**
262	 * Method to find a taxonomy node in a branch.
263	 *
264	 * @param   string  $branch  The branch to search.
265	 * @param   string  $title   The title of the node.
266	 *
267	 * @return  mixed  Integer id on success, null on no match.
268	 *
269	 * @since   2.5
270	 * @throws  Exception on database error.
271	 */
272	public static function getNodeByTitle($branch, $title)
273	{
274		$db = JFactory::getDbo();
275
276		// Set user variables
277		$groups = implode(',', JFactory::getUser()->getAuthorisedViewLevels());
278
279		// Create a query to get the node.
280		$query = $db->getQuery(true)
281			->select('t1.*')
282			->from($db->quoteName('#__finder_taxonomy') . ' AS t1')
283			->join('INNER', $db->quoteName('#__finder_taxonomy') . ' AS t2 ON t2.id = t1.parent_id')
284			->where('t1.access IN (' . $groups . ')')
285			->where('t1.state = 1')
286			->where('t1.title LIKE ' . $db->quote($db->escape($title) . '%'))
287			->where('t2.access IN (' . $groups . ')')
288			->where('t2.state = 1')
289			->where('t2.title = ' . $db->quote($branch));
290
291		// Get the node.
292		$db->setQuery($query, 0, 1);
293
294		return $db->loadObject();
295	}
296
297	/**
298	 * Method to remove map entries for a link.
299	 *
300	 * @param   integer  $linkId  The link to remove.
301	 *
302	 * @return  boolean  True on success.
303	 *
304	 * @since   2.5
305	 * @throws  Exception on database error.
306	 */
307	public static function removeMaps($linkId)
308	{
309		// Delete the maps.
310		$db = JFactory::getDbo();
311		$query = $db->getQuery(true)
312			->delete($db->quoteName('#__finder_taxonomy_map'))
313			->where($db->quoteName('link_id') . ' = ' . (int) $linkId);
314		$db->setQuery($query);
315		$db->execute();
316
317		return true;
318	}
319
320	/**
321	 * Method to remove orphaned taxonomy nodes and branches.
322	 *
323	 * @return  integer  The number of deleted rows.
324	 *
325	 * @since   2.5
326	 * @throws  Exception on database error.
327	 */
328	public static function removeOrphanNodes()
329	{
330		// Delete all orphaned nodes.
331		$db = JFactory::getDbo();
332		$query     = $db->getQuery(true);
333		$subquery  = $db->getQuery(true);
334		$subquery1 = $db->getQuery(true);
335
336		$subquery1->select($db->quoteName('t.id'))
337			->from($db->quoteName('#__finder_taxonomy', 't'))
338			->join('LEFT', $db->quoteName('#__finder_taxonomy_map', 'm') . ' ON ' . $db->quoteName('m.node_id') . '=' . $db->quoteName('t.id'))
339			->where($db->quoteName('t.parent_id') . ' > 1 ')
340			->where($db->quoteName('m.link_id') . ' IS NULL');
341
342		$subquery->select($db->quoteName('id'))
343			->from('(' . $subquery1 . ') temp');
344
345		$query->delete($db->quoteName('#__finder_taxonomy'))
346			->where($db->quoteName('id') . ' IN (' . $subquery . ')');
347
348		$db->setQuery($query);
349		$db->execute();
350
351		return $db->getAffectedRows();
352	}
353
354	/**
355	 * Method to store a node to the database.  This method will accept either a branch or a node.
356	 *
357	 * @param   object  $item  The item to store.
358	 *
359	 * @return  boolean  True on success.
360	 *
361	 * @since   2.5
362	 * @throws  Exception on database error.
363	 */
364	protected static function storeNode($item)
365	{
366		$db = JFactory::getDbo();
367
368		// Check if we are updating or inserting the item.
369		if (empty($item->id))
370		{
371			// Insert the item.
372			$db->insertObject('#__finder_taxonomy', $item, 'id');
373		}
374		else
375		{
376			// Update the item.
377			$db->updateObject('#__finder_taxonomy', $item, 'id');
378		}
379
380		return true;
381	}
382}
383