<?php
/*======================================================================*\
|| #################################################################### ||
|| # vBulletin 4.0.3
|| # ---------------------------------------------------------------- # ||
|| # Copyright ©2000-2010 vBulletin Solutions Inc. All Rights Reserved. ||
|| # This file may not be redistributed in whole or significant part. # ||
|| # ---------------- VBULLETIN IS NOT FREE SOFTWARE ---------------- # ||
|| # http://www.vbulletin.com | http://www.vbulletin.com/license.html # ||
|| #################################################################### ||
\*======================================================================*/

class vB_BlockType_Tagcloud extends vB_BlockType
{
	/**
	 * The Productid that this block type belongs to
	 * Set to '' means that it belongs to vBulletin forum
	 *
	 * @var string
	 */
	protected $productid = '';

	/**
	 * The title of the block type
	 * We use it only when reload block types in admincp.
	 * Automatically set in the vB_BlockType constructor.
	 *
	 * @var string
	 */
	protected $title = '';

	/**
	 * The description of the block type
	 * We use it only when reload block types in admincp. So it's static.
	 *
	 * @var string
	 */
	protected $description = '';

	/**
	 * The block settings
	 * It uses the same data structure as forum settings table
	 * e.g.:
	 * <code>
	 * $settings = array(
	 *     'varname' => array(
	 *         'defaultvalue' => 0,
	 *         'optioncode'   => 'yesno'
	 *         'displayorder' => 1,
	 *         'datatype'     => 'boolean'
	 *     ),
	 * );
	 * </code>
	 * @see print_setting_row()
	 *
	 * @var string
	 */
	protected $settings = array(
		'tagcloud_type' => array(
			'defaultvalue' => 'usage',
			'optioncode'   => 'radio:piped
usage|by_usage
search|by_search',
			'displayorder' => 1,
			'datatype'     => 'string'
		),
		'tagcloud_limit' => array(
			'defaultvalue' => 30,
			'displayorder' => 2,
			'datatype'     => 'integer'
		),
	);

	public function getHTML()
	{
		// Overwrite $vbulletin->options['tagcloud_tags']
		$this->registry->options['tagcloud_tags'] = $this->config['tagcloud_limit'];

		$tag_cloud = $this->fetch_tagcloud($this->config['tagcloud_type']);

		if ($tag_cloud)
		{
			$templater = vB_Template::create('block_tagcloud');
				$templater->register('blockinfo', $this->blockinfo);
				$templater->register('tagcloud', $tag_cloud['links']);
			return $templater->render();
		}
		
	}

	private function fetch_tagcloud($type = 'usage')
	{
		$vbulletin = &$this->registry;

		$tags = array();

		if ($vbulletin->options['tagcloud_usergroup'] > 0 AND !isset($vbulletin->usergroupcache[$vbulletin->options['tagcloud_usergroup']]))
		{
			// handle a usergroup being deleted: default to live permission checking
			$vbulletin->options['tagcloud_usergroup'] = -1;
		}

		require_once(DIR . '/includes/class_taggablecontent.php');
		$collection = new vB_Collection_ContentType();
		$collection->filterTaggable(true);

		//create dummy content item objects.  We use these to call a couple of (what? - Darren)
		$type_objects = array();
		foreach ($collection AS $contenttype)
		{
			$type_objects[$contenttype->getID()] = vB_Taggable_Content_Item::create($vbulletin, $contenttype->getID(), null);
		}
		unset($collection, $contenttype);

		$cacheable = true;
		foreach ($type_objects AS $content)
		{
			if (!$content->is_cloud_cachable())
			{
				$cacheable = false;
				break;
			}
		}

		if (!$cacheable)
		{
			$cloud = null;
		}
		else
		{
			switch ($type)
			{
				case 'search':
					if (isset($vbulletin->searchcloud)) {
						$cloud = $vbulletin->searchcloud;
					}
					break;

				case 'usage':
				default:
					$cloud = $vbulletin->tagcloud;
					break;
			}
		}

		$cloud = null;
		if (!is_array($cloud) OR $cloud['dateline'] < (TIMENOW - (60 * $vbulletin->options['tagcloud_cachetime'])))
		{
			if ($type == 'search')
			{
				$tags_result = $vbulletin->db->query_read_slave("
					SELECT tagsearch.tagid, tag.tagtext, COUNT(*) AS searchcount
					FROM " . TABLE_PREFIX . "tagsearch AS tagsearch
					INNER JOIN " . TABLE_PREFIX . "tag AS tag ON (tagsearch.tagid = tag.tagid)
					" . ($vbulletin->options['tagcloud_searchhistory'] ?
						"WHERE tagsearch.dateline > " . (TIMENOW - (60 * 60 * 24 * $vbulletin->options['tagcloud_searchhistory'])) :
						'') . "
					GROUP BY tagsearch.tagid, tag.tagtext
					ORDER BY searchcount DESC
					LIMIT " . $vbulletin->options['tagcloud_tags']
				);
			}
			else
			{
				//get the query bits from the type objects.  If two objects return the same exact join/where information
				//we can collapse the subqueries.  This is particularly useful for the cms content types which are
				//largely the same under the hood.
				$bit_ids = array();
				$bit_values = array();
				foreach ($type_objects AS $type => $content)
				{
					$contenttypeid = vB_Types::instance()->getContentTypeID($type);
					$bits = $content->fetch_tag_cloud_query_bits();
					if ($bits)
					{
						$pos = array_search($bits, $bit_values);
						if ($pos === false)
						{
							$bit_ids[] = array($contenttypeid);
							$bit_values[] = $bits;
						}
						else
						{
							$bit_ids[$pos][] = $contenttypeid;
						}
					}
				}

				//build the subqueries from the bits.
				$subqueries = array();
				foreach ($bit_values AS $key => $bits)
				{
					$timelimit = (TIMENOW - (60 * 60 * 24 * $vbulletin->options['tagcloud_usagehistory']));
					$query = 	"
						SELECT tagcontent.tagid, tag.tagtext, COUNT(*) AS searchcount
						FROM " . TABLE_PREFIX . "tagcontent AS tagcontent
						INNER JOIN " . TABLE_PREFIX . "tag AS tag ON (tagcontent.tagid = tag.tagid) " .
						implode("\n", $bits['join']) . "
						WHERE tagcontent.contenttypeid IN (" . implode(",", $bit_ids[$key]) . ") AND
							tagcontent.dateline > $timelimit AND " .
							implode(" AND ", $bits['where']) . "
						GROUP BY tagcontent.tagid, tag.tagtext
					";
					$subqueries[] = $query;
				}

				if (count($subqueries))
				{
					$query = "
						SELECT data.tagid, data.tagtext, SUM(data.searchcount) AS searchcount
						FROM
							(" . implode(" UNION ALL ", $subqueries) . ") AS data
						GROUP BY data.tagid, data.tagtext
						ORDER BY searchcount DESC
						LIMIT " . $vbulletin->options['tagcloud_tags'];

					$tags_result = $vbulletin->db->query_read_slave($query);
					while ($currenttag = $vbulletin->db->fetch_array($tags_result))
					{
						$tags["$currenttag[tagtext]"] = $currenttag;
						$totals[$currenttag['tagid']] = $currenttag['searchcount'];
					}
				}
			}

			while ($currenttag = $vbulletin->db->fetch_array($tags_result))
			{
				$tags["$currenttag[tagtext]"] = $currenttag;
				$totals[$currenttag['tagid']] = $currenttag['searchcount'];
			}

			// fetch the stddev levels
			$levels = fetch_standard_deviated_levels($totals, $vbulletin->options['tagcloud_levels']);

			// assign the levels back to the tags
			foreach ($tags AS $tagtext => $tag)
			{
				$tags[$tagtext]['level'] = $levels[$tag['tagid']];
				$tags[$tagtext]['tagtext_url'] = urlencode(unhtmlspecialchars($tag['tagtext']));
			}

			// sort the categories by title
			uksort($tags, 'strnatcasecmp');

			$cloud = array(
				'tags' => $tags,
				'count' => sizeof($tags),
				'dateline' => TIMENOW
			);

			if ($cacheable)
			{
				if ($type == 'search' OR $type == 'selectlist')
				{
					$vbulletin->searchcloud = $cloud;
				}
				else
				{
					$vbulletin->tagcloud = $cloud;
				}
			}
		}

		if (empty($cloud['tags']))
		{
			return '';
		}

		$cloud['links'] = '';

		foreach ($cloud['tags'] AS $thistag)
		{
			$templater = vB_Template::create('tag_cloud_link');
				$templater->register('thistag', $thistag);
			$cloud['links'] .= $templater->render();
		}
		return $cloud;
	}
}