PageRenderTime 98ms CodeModel.GetById 39ms app.highlight 35ms RepoModel.GetById 15ms app.codeStats 1ms

/halogy/application/libraries/Tags.php

https://bitbucket.org/haloweb/halogy-1.0/
PHP | 920 lines | 455 code | 142 blank | 323 comment | 58 complexity | dcc5639d53fb248525d6276297316b05 MD5 | raw file
  1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2/**
  3 * CodeIgniter
  4 *
  5 * An open source application development framework for PHP 4.3.2 or newer
  6 *
  7 * @package		CodeIgniter
  8 * @author		ExpressionEngine Dev Team
  9 * @copyright	Copyright (c) 2006, EllisLab, Inc.
 10 * @license		http://codeigniter.com/user_guide/license.html
 11 * @link		http://codeigniter.com
 12 * @since		Version 1.0
 13 * @filesource
 14 */
 15
 16// ------------------------------------------------------------------------
 17
 18/**
 19 * CX Tags Class Modified by Haloweb
 20 *
 21 * @package		CXTags
 22 * @category	Models
 23 * @author		David Pennington <xeoncross.com>
 24 * @link		http://code.google.com/p/cxtags/
 25 * @license		http://www.gnu.org/copyleft/gpl.html
 26 * @version		1.1.0
 27 *
 28 * This class allows any table, and any type of system, to
 29 * add, edit, and delete tags from any object/row without
 30 * interfering with other tables that are also using tags.
 31 *
 32 *
 33 * Forexample, you have 58 articles on your blog that each
 34 * have 2-4 tags attached to them. Now you add a "Image Gallery"
 35 * to your site to showoff your photos. You want to "tag" each
 36 * photo with some key words (city, dark, night) that will help
 37 * people sort through all the images. With this class both
 38 * your articles and your images (and anything else you add) can
 39 * use the same "tags" table without interfering with each other.
 40 * Just because blog article 23 has the tag "sports" doesn't mean
 41 * that image 23 will return the tag "sports".
 42 *
 43 *
 44 *
 45 * About the variables:
 46 * (the fictitious tables "posts" and "images" used for examples)
 47 *
 48 * TABLE
 49 * The name of a table using tags (i.e. "posts", "images", etc..)
 50 * this allows us to tell the difference between a tag for row
 51 * 23 of the "images" table and row 23 of the "posts" table.
 52 *
 53 * ROW_ID
 54 * The row_id is the unique ID of a row from the table set in TABLE.
 55 * This id corasponds to a some row like "row 23" of the "posts" table.
 56 *
 57 * siteID
 58 * The ID of the user that created the tag. (for use with a "users" table)
 59 * This means that multiple users can each tag a TABLE row with their own
 60 * tags. This is optional so if you don't plan on starting another "de
 61 *
 62 * SAFE_TAG
 63 * This is a URL/file/etc. safe version of the TAG. (only [a-z0-9_])
 64 *
 65 * TAG
 66 * A cleaned (alphanumeric and spaces) catialized tag (only for display)
 67 *
 68 * TAG_ID
 69 * The Unique Id of the tag
 70 *
 71 * DATE
 72 * The date the tag was attached to a row
 73 *
 74 *
 75 * ABOUT THE CLASS FUNCTIONS
 76 * The following functions need a table name and a safe_tag
 77 * string or a tag_id to work. Never try to pass the plain "tag"
 78 * field to any of these functions as it won't work.
 79 *
 80 *
 81/***************************** MySQL TABLES
 82
 83CREATE TABLE IF NOT EXISTS `ci_tags` (
 84  `id` int NOT NULL auto_increment,
 85  `safe_tag` varchar(30) NOT NULL,
 86  `tag` varchar(40) NOT NULL,
 87  PRIMARY KEY  (`id`),
 88  UNIQUE KEY `safe_tag` (`safe_tag`)
 89) ENGINE=MyISAM ;
 90
 91CREATE TABLE IF NOT EXISTS `ci_tags_ref` (
 92  `tag_id` int unsigned NOT NULL,
 93  `row_id` int unsigned NOT NULL,
 94  `siteID` int unsigned NOT NULL,
 95  `date` int unsigned NOT NULL,
 96  `table` varchar(20) NOT NULL
 97) ENGINE=MyISAM ;
 98
 99****************************************** 
100 */
101
102class Tags {
103
104	//The names of the tables
105	var $CI;	
106	var $tags_ref			= 'tags_ref';
107	var $tags				= 'tags';
108	var $tags_prefix		= null;
109	var $tags_ref_prefix	= null;
110	var $siteID				= null;
111	
112	function Tags() {
113		
114		$this->CI =& get_instance();
115		
116		//prefix the tags tables with the right thing
117		$this->tags_prefix		= $this->CI->db->dbprefix($this->tags);
118		$this->tags_ref_prefix	= $this->CI->db->dbprefix($this->tags_ref);
119
120		// get siteID, if available
121		if (defined('SITEID'))
122		{
123			$this->siteID = SITEID;
124		}		
125	}
126	
127	/*
128	 * Fetch all tags for a given row/object and table
129	 *
130	 * $data = array(
131	 * 		'table' => 'posts',	//Name of the table row_id is from
132	 * 		'row_id' => '1',	//ID of the row that we want the tags for
133	 *		'siteID' => null,	//Optional ID of a single user
134	 *		'limit' => 10,		//Optional Max number of results
135	 *		'offset' => 0		//Optional Offset of results
136	 *	);
137	 * @param	array	data about the tag(s) we are fetching
138	 * @return	mixed
139	 */
140	function fetch_tags($data) {
141		//If there is no table
142		if(!$data['table'] || !$data['row_id']) {
143			return;
144		}
145
146		//Select the Tag info
147		$this->CI->db->select('tag, safe_tag, id, date');
148		$this->CI->db->distinct();
149		$this->CI->db->join($this->tags, 
150			$this->tags_ref_prefix. '.tag_id = '. $this->tags_prefix. '.id', 'inner');
151		$this->CI->db->where('table', $data['table']);
152		$this->CI->db->where('row_id', $data['row_id']);
153
154		//If a limit is implied
155		if(isset($data['limit']) && $data['limit']) {
156			$this->CI->db->limit($data['limit'],
157			(isset($data['offset']) ? $data['offset'] : null)
158			);
159		}
160
161		//If a siteID is given
162		if(isset($data['siteID']) && $data['siteID']) {
163			$this->CI->db->where($this->tags_ref_prefix. '.siteID', $data['siteID']);
164		}
165
166		$result = $this->CI->db->get($this->tags_ref);
167
168		if ($result->num_rows())
169		{
170			return $result->result_array();
171		}
172		else
173		{
174			return FALSE;
175		}
176
177	}
178
179	
180
181	/*
182	 * Fetch the most popular/used tags
183	 *
184	 * $data = array(
185	 * 		'table' => 'posts',	//Name of the table row_id is from
186	 *		'siteID' => null,	//Optional ID of a single user
187	 *		'limit' => 10,		//Optional Max number of results
188	 *		'offset' => 0		//Optional Offset of results
189	 *	);
190	 * @param	array	data about the tag(s) we are fetching
191	 * @return	mixed
192	 */
193	function fetch_popular_tags($data) {
194		//If there is no table
195		if(!$data['table']) {
196			return;
197		}
198
199		//Select the Tag info
200		$this->CI->db->select('tag, safe_tag, id, date, COUNT(*) as count');
201		//$this->CI->db->distinct();
202		$this->CI->db->join($this->tags, 
203			$this->tags_ref_prefix. '.tag_id = '. $this->tags_prefix. '.id', 'inner');
204		$this->CI->db->where('table', $data['table']);
205		$this->CI->db->order_by('count DESC, tag ASC');
206		$this->CI->db->group_by('tag');
207
208		//If a limit is NOT implied
209		if(!isset($data['limit']) || !$data['limit']) {
210			$data['limit'] = 50;
211		}
212
213		//Only fetch up to limit number of rows
214		$this->CI->db->limit($data['limit'],
215		(isset($data['offset']) ? $data['offset'] : null)
216		);
217
218		//If a siteID is given
219		if(isset($data['siteID']) && $data['siteID']) {
220			$this->CI->db->where($this->tags_ref_prefix. '.siteID', $data['siteID']);
221		}
222
223		$result = $this->CI->db->get($this->tags_ref);
224		
225		if ($result->num_rows())
226		{
227			return $result->result_array();
228		}
229		else
230		{
231			return FALSE;
232		}
233
234	}
235
236
237
238	/*
239	 * Get tag data based on tag_id OR safe_tag
240	 *
241	 * @param	string	Name of the table to use
242	 * @param	mixed	Int (tag_id) or string (safe_tag)
243	 * @return	mixed
244	 */
245	function fetch_tag($piece=null) {
246
247		//If no tag is given
248		if(!$piece) { return; }
249
250		//Is the $piece an ID or a TAG name?
251		if(is_int($piece)) {
252			$this->CI->db->where('id', $piece);
253		} else {
254			$this->CI->db->where('safe_tag', $piece);
255		}
256
257		$result = $this->CI->db->get($this->tags);
258		
259		if ($result->num_rows())
260		{
261			return $result->result_array();
262		}
263		else
264		{
265			return FALSE;
266		}
267
268	}
269
270	
271
272	/*
273	 * Fetch all rows/objects that use one or more safe_tag(s) or tag_id(s)
274	 *
275	 * $data = array(
276	 * 		'table' => 'posts',	//Name of the table row_id is from
277	 * 		'tags' => '1',		//tag_id, tag, or an array of either
278	 * 		'siteID' = > null,	//Optional only return rows for siteID
279	 *		'limit' => 10,		//Optional Max number of results
280	 *		'offset' => 0		//Optional Offset of results
281	 *	);
282	 * @param	array	data about the tag(s) we are fetching
283	 * @return	mixed
284	 */
285	function fetch_rows($data) {
286		//If there is no table
287		if(!$data['table'] || !$data['tags']) {
288			return;
289		}
290
291		//Add the WHERE clause for the tags
292		$this->where_tags($data['tags']);
293
294		//Select the Tag info
295		//$this->CI->db->select('tag, safe_tag, date, row_id, siteID');
296		//Don't need tag/user info because the GROUP BY will only show 1
297		//tag/user for each row (even if there are may)
298		$this->CI->db->select('row_id');
299		$this->CI->db->group_by("row_id");
300		$this->CI->db->join($this->tags_ref, 
301		$this->tags_ref_prefix. '.tag_id = '. $this->tags_prefix. '.id', 'inner');
302		$this->CI->db->where('table', $data['table']);
303
304		//If a limit is implied
305		if(isset($data['limit']) && $data['limit']) {
306			$this->CI->db->limit($data['limit'],
307			(isset($data['offset']) ? $data['offset'] : null)
308			);
309		}
310
311		//If a siteID is given
312		if(isset($data['siteID']) && $data['siteID']) {
313			$this->CI->db->where($this->tags_ref_prefix. '.siteID', $data['siteID']);
314		}
315
316		$result = $this->CI->db->get($this->tags);
317		
318		if ($result->num_rows())
319		{
320			return $result->result_array();
321		}
322		else
323		{
324			return FALSE;
325		}
326
327	}
328
329
330
331	/*
332	 * Count all the tags for a user and/or table
333	 *
334	 * $data = array(
335	 * 		'table' => 'posts',	//Name of the table row_id is from
336	 *		'siteID' => null,	//Optional ID of a single user
337	 *	);
338	 * @param	array	data about the tag(s) we are counting
339	 * @return	mixed
340	 */
341	function count_tags($data) {
342		//If there is no table
343		if(!$data['table']) {
344			return;
345		}
346
347		/*
348		 * SELECT COUNT(DISTINCT `safe_tag`) as count FROM ci_tags
349		 * INNER JOIN ci_tags_ref ON (id = tag_id) WHERE `siteID` = 0 AND `table` = 'posts'
350		 */
351
352		//Select the Tag info
353		$this->CI->db->select('COUNT(*) as count');
354		$this->CI->db->join($this->tags, 
355			$this->tags_ref_prefix. '.tag_id = '. $this->tags_prefix. '.id', 'inner');
356		$this->CI->db->where('table', $data['table']);
357		$this->CI->db->order_by('count DESC');
358
359		//If a siteID is given
360		if(isset($data['siteID']) && $data['siteID']) {
361			$this->CI->db->where($this->tags_ref_prefix. '.siteID', $data['siteID']);
362		}
363
364		//fetch the number of rows
365		$result = $this->CI->db->get($this->tags_ref);
366
367		return $result->result();
368
369	}
370	
371	
372	
373	/*
374	 * Insert tags for a row/object
375	 *
376	 * $data = array(
377	 * 		'table' => 'posts',	//Name of the table row_id is from
378	 * 		'tags' => array('tag 1', 'tag 2'),	//An array of tags
379	 * 		'row_id' => 23,
380	 * 		'siteID' = > null,	//Optional only return rows for siteID
381	 *	);
382	 * @param	array	data about the tag(s) we are creating
383	 * @return	mixed
384	 */
385	function add_tags($data=array()) {
386
387		//If there is no table, row, or tags...
388		if(!$data['table'] || !$data['tags'] || !$data['row_id']) {
389			return;
390		}
391
392		//The array of tags -minus the $finalized_tags
393		$tags = array();
394		//This will store the table ID and safe_tag of each tag
395		$finalized_tags = array();
396		//This will store the "Cleaned" version for our where_tag() function
397		$safe_tags		= array();
398
399
400		//STEP 1: Create the "safe" version of each tag
401		foreach($data['tags'] as $key => $tag) {
402
403			$safe_tag = $this->make_safe_tag($tag);
404				
405			//Add this tag to an array called $tags
406			$tags[$safe_tag] = trim($tag);
407
408			//Add it to an array of ONLY safe_tags
409			$safe_tags[] = $safe_tag;
410
411		}
412
413
414		//STEP 2: Search DB for the tags already in there
415		$this->CI->db->select('id, tag, safe_tag');
416		$this->where_tags($safe_tags);
417		$query = $this->CI->db->get($this->tags);
418
419		//If some of these tags already exist
420		if ($query->num_rows() > 0) {
421			foreach ($query->result() as $row) {
422
423				//Add this row to the finalized tags
424				$finalized_tags[$row->safe_tag] = array(
425		   			'tag' => $row->tag,
426		   			'id' => $row->id
427				);
428					
429			}
430				
431			/**
432			 * Now that we have an array of the tags from the DB
433			 * we can unset them from the $tags array so they aren't
434			 * added to the table a second time!
435			 */
436			 
437			foreach($finalized_tags as $safe_tag => $tag) {
438				if(isset($tags[$safe_tag])) {
439					unset($tags[$safe_tag]);
440				}
441			}		
442		}
443
444		//STEP 3: Insert each tag into our table since it isn't there already
445		foreach($tags as $safe_tag => $tag) {
446			
447			// tidy tag
448			$tag = ucwords(strtolower($tag));
449
450			//Insert the tag into the database
451			$this->CI->db->insert($this->tags, array('safe_tag' => $safe_tag, 'tag' => $tag));
452				
453			//Now that the tag is in the DB we need to add it to the finalized tags
454			$finalized_tags[$safe_tag] = array('tag' => $tag, 'id' => $this->CI->db->insert_id());
455		}
456
457
458		//STEP 4: Attach each tag to the row
459
460		//Row data that won't change doesn't need to be repeated!
461		$row_data = array(
462			'`table`' => $data['table'],
463			'row_id' => $data['row_id']
464		);
465
466		/* MOD: haloweb -- dont duplicate data in row by getting tags
467		if ($tags_data = $this->fetch_tags($row_data))
468		{
469			foreach ($tags_data as $row)
470			{
471				$row_tags[$row['safe_tag']] = $row['tag'];
472			}
473		}
474		*/
475		
476		//If a siteID is given
477		if(isset($data['siteID']) && $data['siteID']) {
478			$row_data['siteID'] = $data['siteID'];
479		}
480
481		//For each tag - attach it to the row/object
482		foreach($finalized_tags as $safe_tag => $tag) {
483				
484			//Data about the row
485			$row_data['date'] = date("Y-m-d H:i:s");
486			$row_data['tag_id'] = $tag['id'];
487				
488			/*
489			if (!in_array($safe_tag, $row_tags))
490			{
491				$this->CI->db->insert($this->tags_ref, $row_data);
492			}
493			*/
494
495			//FINALLY INSERT THE ROW! ...(jeez)...
496			$this->CI->db->insert($this->tags_ref, $row_data);
497		}
498
499		//return true;
500
501		// Not sure why someone would need this..
502		// but return the finalized array
503		return $finalized_tags;
504
505	}
506
507
508
509	/*
510	 * Delete tag relationships to a row/object
511	 *
512	 * $data = array(
513	 * 		'table' => 'posts',	//Name of the table row_id is from
514	 * 		'row_id' => 23,
515	 * 		'siteID' => null	//Optional only delete rows for siteID
516	 *	);
517	 * @param	array	data about the tag_ref we are deleting
518	 * @return	mixed
519	 */
520	function delete_tag_ref($data) {
521		//If there is no table or row_id
522		if(!$data['table'] || !$data['row_id']) {
523			return;
524		}
525
526		//If a user is set - only delete tags for that user
527		if(isset($data['siteID']) && $data['siteID']) {
528			$this->CI->db->where('siteID', $data['siteID']);
529		}
530
531		//Delete all tags_ref where this table and row are found
532		$this->CI->db->where('table', $data['table']);
533		$this->CI->db->where('row_id', $data['row_id']);
534		$this->CI->db->delete($this->tags_ref);
535
536		//return the rows deleted
537		$rows = $this->CI->db->affected_rows();
538
539		//Delete tags that are no-longer referenced by any row
540		$this->delete_tags();
541
542		return $rows;
543
544	}
545
546
547	
548	/*
549	 * Delete all tags from a given user
550	 *
551	 * $data = array(
552	 * 		'table' => 'posts',	//Name of the table row_id is from
553	 * 		'siteID' = > null,	//Optional only delete rows for siteID
554	 *	);
555	 * @param	array	data about the tag_ref we are deleting
556	 * @return	mixed
557	 */
558	function delete_user_tags($data) {
559		//If there is no table or row_id
560		if(!$data['table'] || !$data['siteID']) {
561			return;
562		}
563
564		//Where the tag is used by this user
565		$this->CI->db->where('siteID', $data['siteID']);
566
567		//Delete all tags_ref where this table and row are found
568		$this->CI->db->delete($this->tags_ref);
569
570		//return the rows deleted
571		$rows = $this->CI->db->affected_rows();
572
573		//Delete tags that are no-longer referenced by any row
574		$this->delete_tags();
575
576		return $rows;
577
578	}
579
580
581
582	/*
583	 * Delete tags not referenced by any row/object.
584	 *
585	 * Because CI does NOT support DELETE...JOIN syntax
586	 * will will have to do a SELECT first and then delete
587	 * the result rows.
588	 *
589	 * @return	Int
590	 */
591	function delete_tags() {
592		/*
593		 //Join it to the tags_ref to make sure no rows are found
594		 $this->CI->db->join('tags_ref', 'tags.id = tags_ref.tag_id');
595
596		 //the tag_id is NULL (not found)
597		 $this->CI->db->where('tag_id', null);
598		 $this->CI->db->delete('tags');
599
600		 //return the rows deleted
601		 return $this->CI->db->affected_rows();
602		 */
603
604		$this->CI->db->select('id');
605		//Join it to the tags_ref to make sure no rows are found
606		$this->CI->db->join($this->tags_ref, 
607			$this->tags_ref_prefix. '.tag_id = '. $this->tags_prefix. '.id', 'left');
608		//the tag_id is NULL (not found)
609		$this->CI->db->where('tag_id', null);
610		$result = $this->CI->db->get($this->tags);
611
612		//If there are NO lost tags - just return
613		if(!$result->num_rows()) {
614			return 0;
615		}
616
617		//Colect the ids
618		foreach($result->result() as $row) {
619			$ids[] = $row->id;
620		}
621
622		//Delete all ids found in this list
623		$this->CI->db->where_in('id', $ids);
624		$this->CI->db->delete($this->tags);
625
626		//return the rows deleted
627		return $this->CI->db->affected_rows();
628
629	}
630
631	function get_tags($table, $ID)
632	{
633		// get tags
634		$tags = $this->fetch_tags(array(
635			'table' => $table,
636			'row_id' => $ID
637		));
638		
639		if ($tags)
640		{
641			foreach($tags as $tag)
642			{
643				$tagsArray[] = $tag['tag'];
644			}
645			return $tagsArray;
646		}
647		else
648		{
649			return FALSE;
650		}
651	}
652	
653	function get_popular_tags($table, $limit = 10)
654	{
655		// get tags
656		$tags = $this->fetch_popular_tags(array(
657			'table' => $table,
658			'siteID' => $this->siteID,
659			'limit' => $limit
660		));
661
662		if ($tags)
663		{
664			return $tags;
665		}
666		else
667		{
668			return FALSE;
669		}
670	}
671	
672	function update_tags($table, $ID = '', $tags = '')
673	{
674		// add tags
675		if ($tags)
676		{
677			$data = array(			 		
678				'table' => $table,
679				'row_id' => $ID
680			);
681
682			if ($this->siteID)
683			{
684				$data['siteID'] = $this->siteID;
685			}
686			
687			$this->delete_tag_ref($data);
688			
689			$tagsArray = explode(',', $tags);
690			foreach($tagsArray as $key => $tag)
691			{
692				$tag = trim($tag);
693				if (isset($tag) && $tags != '' && strlen($tag) > 0)
694				{
695					$tidyTagsArray[] = $tag;
696				}
697			}
698			$tags = array(
699		 		'table' => $table,
700		 		'tags' => $tidyTagsArray,
701				'row_id' => $ID
702			);
703			if ($this->siteID)
704			{
705				$tags['siteID'] = $this->siteID;
706			}			
707			$this->add_tags($tags);
708
709			return TRUE;
710		}
711		else
712		{
713			return FALSE;
714		}
715	}
716
717	function search($table, $tags = '')
718	{
719		$tags = explode(' ', $tags);
720		
721		$tagdata = array(
722	 		'table' => $table,				//Name of the table row_id is from
723	 		'tags' => $tags,				//tag_id, tag, or an array of either
724	 		'limit' => null,				//Optional Max number of results
725			'offset' => null,				//Optional Offset of results
726		);
727		if ($this->siteID)
728		{
729			$tagdata['siteID'] = $this->siteID;
730		}
731
732		if ($object = $this->fetch_rows($tagdata))
733		{
734			if ($rows = $object->result())
735			{
736				foreach($rows as $row => $key)
737				{
738					$ids[] = $key->row_id;
739				}
740
741				return $ids;
742			}
743			else
744			{
745				return FALSE;
746			}
747		}
748		else
749		{
750			return FALSE;
751		}
752	}
753
754	function tag_cloud($table, $limit = 10, $maxSize = 150, $minSize = 100)
755	{				
756		// get tag data
757		$tagdata = array(
758			'table' => $table,
759			'siteID' => $this->siteID,
760			'limit' => $limit
761		);
762		$result = $this->fetch_popular_tags($tagdata);
763		$tags = $result->result_array();	
764
765		// populate tag count array
766		if ($tags)
767		{
768			// get sizes of tags
769			foreach($tags as $tag)
770			{
771				$tagsCount[$tag['tag']] = $tag['count'];
772			}
773
774			// get the largest and smallest array values
775			$maxQty = max(array_values($tagsCount));
776			$minQty = min(array_values($tagsCount));
777	
778			// find range of values
779			$range = $maxQty['count'] - $minQty['count'];
780			
781			// don't divide by zero
782			if (0 == $range)
783			{
784				$range = 1;
785			}
786
787			// determine the font-size increment
788			$step = ($maxSize - $minSize) / ($range);
789
790			// populate tag array
791			foreach($tags as $tag)
792			{
793				$size = $minSize + (($tag['count'] - $minQty) * $step);
794				$tagsArray[$tag['tag']] = array('safe_tag' => $tag['safe_tag'], 'size' => $size);
795			}			
796
797			// load output vars
798			$data['tags'] = $tagsArray;
799			$data['step'] = $step;
800			$data['maxQty'] = $maxQty;
801			$data['minQty'] = $minQty;
802			$data['maxSize'] = $maxSize;
803			$data['minSize'] = $minSize;
804
805			return $data;
806		}
807		else
808		{
809			return FALSE;
810		}
811	}
812
813
814	/*
815	 * SUPPORT FUNCTIONS
816	 */
817
818
819
820	/*
821	 * Turn a comma-separated string into an array of elements
822	 * @param	string	The string from an input box or something
823	 * @return	array
824	 */
825	function comma_to_array($string='') {
826		/*
827		 //Can handle even the most messed-up comma strings like below:
828		 $tags = "\n\r". 'tag1, this is tag2, or tag3. but we can\'t tag4, tag5, other '. "\n".
829		 'tag6, "plus tag7", #tag8,'. "\n\n". ',,,, ,,,,, ,,,'. "\n". ',, '.
830		 "\n\n\n". '< this is another, tag9.,, , ';
831		 */
832
833		//Make the String lowercase
834		$string = trim(strtolower($string));
835
836		//Replace anything that isn't a letter, comma, space, quote, or number!
837		$string = preg_replace("/[^a-z0-9, \"']/", '', $string);
838
839		//Remove empty "," so that we don't make empty elements
840		$string = preg_replace("/,[^a-z0-9]*,/", ',', $string);
841
842		//If there is an ending comma.... kill it!
843		$string = rtrim($string, ',');
844
845		//Turn the string into an array of tags
846		$string = explode(',', $string);
847
848		//Remove extra spaces from front and back of each element and capitalize
849		foreach($string as $key => $tag) {
850			$string[$key] = trim(ucwords($tag));
851		}
852
853		return $string;
854	}
855
856
857
858	/*
859	 * Make a tag safe for file & URL usage
860	 * @param	string	the tag to clean
861	 * @return	string	cleaned tag
862	 */
863	function make_safe_tag($tag='') {
864		$tag = strtolower($tag);
865		//remove anything not alphanumeric OR "_"
866		$tag = preg_replace("/([^a-z0-9_\-]+)/i", '-', $tag);
867		//remove duplicate "_"
868		$tag = preg_replace("/(-{2,})+/", '-', $tag);
869		//remove posible start/end "_"
870		$tag = trim($tag, '-');
871		return $tag;
872	}
873
874
875
876	/*
877	 * Adds a WHERE Clause to a query. Pass this function a single
878	 * tag_id/safe_tag - or an array of tag_ids/safe_tags.
879	 *
880	 * @param	mixed	string or array of tag_id's or safe_tags
881	 */
882	function where_tags($tags) {
883		//If we have been given an array of tags to match
884		if(is_array($tags)) {
885			$ints = null;
886			$strings = null;
887			//Check each tag to see if it is an ID or a name
888			foreach($tags as $tag) {
889				if(is_int($tag)) {
890					$ints[] = $tag;
891				} else {
892					$strings[] = $tag;
893				}
894			}
895
896			//If some ID's were given
897			if($ints) {
898				$this->CI->db->where_in('id', $ints);
899			}
900
901			//If some tag names where given
902			if($strings) {
903				//If Int's are in the query we need an OR clause
904				if($ints) {
905					$this->CI->db->or_where_in('safe_tag', $strings);
906				} else {
907					$this->CI->db->where_in('safe_tag', $strings);
908				}
909			}
910			//Else we are just looking for a rows that match one tag/ID
911		} else {
912			if(is_int($tags)) {
913				$this->CI->db->where('id', $tags);
914			} else {
915				$this->CI->db->where('safe_tag', $tags);
916			}
917		}
918	}
919
920}