root/trunk/phptagengine.class.inc.php

Revision 24 (checked in by svn, 2 years ago)

only show errors if debug enabled

Line 
1 <?php
2
3 // Copyright (c) 2006-2007 Alex King. All rights reserved.
4 // http://alexking.org/projects/php-tag-engine
5 //
6 // Released under the LGPL license
7 // http://www.opensource.org/licenses/lgpl-license.php
8 //
9 // **********************************************************************
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // **********************************************************************
14
15 /**
16  * This file contains the PHP Tag Engine class definition.
17  *
18  * @package phptagengine
19  */
20
21 if (__FILE__ == basename($_SERVER['SCRIPT_NAME'])) { die(); }
22
23 /**
24  * The phptagengine class, all the good stuff happens here.
25  *
26  * @package phptagengine
27  */
28 class phptagengine {
29         /**
30          * set to global $ADOdb instance
31          * @var mixed
32          */
33         var $db;
34         /**
35          * Column name escape string for the database being used. Checks for: mysql, postgres7, mssql
36          *
37          * @var string
38          */
39         var $db_col_escape_char;
40         /**
41          * Name of the tags table in the database
42          *
43          * @var string
44          */
45         var $table_tags;
46         /**
47          * Name of the tag names table in the database
48          *
49          * @var string
50          */
51         var $table_tag_names;
52         /**
53          * Name of the users table in the database
54          *
55          * @var string
56          */
57         var $table_users;
58         /**
59          * The display name column (normally name or username) in the users table in the database
60          *
61          * @var string
62          */
63         var $table_users_name;
64         /**
65          * URL to the PHP Tag Engine folder, include the trailing slash
66          *
67          * @var string
68          */
69         var $base_url;
70         /**
71          * URL of the file handling AJAX requests
72          *
73          * @var string
74          */
75         var $ajax_handler;
76         /**
77          * Name of language file to include - languages/(language).inc.php
78          *
79          * @var string
80          */
81         var $language;
82         /**
83          * Character set
84          *
85          * @var string
86          */
87         var $charset;
88         /**
89          * Array of strings for localization
90          *
91          * @var array
92          */
93         var $strings;
94         /**
95          * Value to be used if no type is passed in, useful if you only have one
96          * type of object you are tagging and just need a default value
97          *
98          * @var string
99          */
100         var $default_type;
101         /**
102          * Value to be used if no user is passed in, useful if you are not tracking
103          * tags by user and just need a default value
104          *
105          * @var string
106          */
107         var $default_user;
108         /**
109          * URL for browsing items by tag, includes token replacement for tag, type
110          *
111          * example: http://example.com/index.php?view=tag&tag=<tag>&type=<type>
112          *
113          * @var string
114          */
115         var $tag_browse_url;
116         /**
117          * Show an X next to each tag to allow removal of that tag
118          *
119          * @var boolean
120          */
121         var $show_remove_links;
122         /**
123          * Show text or image?
124          *
125          * example: 'text' - uses @link phptagengine::$strings
126          * example: 'image'
127          *
128          * @var string
129          */
130         var $edit_button_display;
131         /**
132          * URL of the image for the edit button
133          *
134          * @var string
135          */
136         var $edit_button_image_url;
137         /**
138          * Show text or image?
139          *
140          * example: 'text' - uses @link phptagengine::$strings
141          * example: 'image'
142          *
143          * @var string
144          */
145         var $delete_button_display;
146         /**
147          * URL of the image for the delete button
148          *
149          * @var string
150          */
151         var $delete_button_image_url;
152         /**
153          * Stores arrays of tags for items, use as a cache to reduce queries
154          *
155          * @var array
156          */
157         var $item_tags_cache;
158         /**
159          * Enable Yahoo! Auto-Complete
160          *
161          * @var boolean
162          */
163         var $yac;
164         /**
165          * List of Yahoo! UI Pattern files to be included, in case Yahoo! UI Patterns are already in use elsewhere and we don't want to duplicate their inclusion. The array should contain filenames in the 'yui' directory.
166          *
167          * Note: order matters in this list.
168          *
169          * @var array
170          */
171         var $yac_files;
172         /**
173          * PHP Tag Engine Version
174          *
175          * @var string
176          */
177         var $version;
178         /**
179          * Show or hide error messages
180          *
181          * @var boolean
182          */
183         var $debug;
184        
185         /**
186          * Initializes the class
187          *
188          * @return phptagengine
189          */
190         function phptagengine() {
191                 $this->base_url = 'http://example.com/';
192                 $this->ajax_handler = 'http://example.com/ajax.php';
193                 $this->language = 'english';
194                 $this->charset = 'UTF-8';
195                 $this->db_type = 'mysql';
196                 $this->strings = array();
197                 $this->tag_browse_url = 'http://example.com/index.php?view=tags&tag=<tag>&type=<type>';
198                 $this->default_type = '';
199                 $this->default_user = 1;
200                 $this->edit_button_display = 'text';
201                 $this->delete_button_display = 'text';
202                 $this->yac = true;
203                 $this->yac_files = array(
204                         'yahoo-dom-event.js'
205                         ,'autocomplete.js'
206                 );
207                 $this->version = '1.01';
208                 $this->debug = false;
209         }
210        
211         /**
212          * Sets the character used for escaping column names for the database type in use
213          */
214         function set_db_col_escape_char() {
215                 switch ($this->db->databaseType) {
216                         case 'mysql':
217                                 $this->db_col_escape_char = '`';
218                                 break;
219                         case 'postgres7':
220                         case 'mssql':
221                                 $this->db_col_escape_char = '"';
222                                 break;
223                 }
224         }
225
226         /**
227          * Sets the type or user property to the default value if the value is
228          * null, could be extended in the future
229          * @param string $prop expected 'type' or 'user'
230          * @param mixed $value if this is null, we set the default
231          */
232         function default_value($prop, $value) {
233                 if ($value == null && in_array($prop, array('type', 'user'))) {
234                         eval('$value = $this->default_'.$prop.';');
235                 }
236                 return $value;
237         }
238        
239         /**
240          * Set a tag to lowercase and remove spaces, could be extended in the future
241          * @param mixed $value the value to be normalized
242          * @param string $type the type of value, used for the switch statement
243          * @return mixed
244          */
245         function normalize($value, $type = 'tag') {
246                 switch ($type) {
247                         case 'tag':
248                                 $value = preg_replace('|[^a-z0-9_.\-@#$%*!&]|i', '', strtolower($value));
249                                 break;
250                 }
251                 return $value;
252         }
253
254         /**
255          * Does a tag already exist
256          *
257          * @uses phptagengine::get_tag_id()
258          *
259          * @param string $tag
260          * @return boolean
261          */
262         function tag_exists($tag) {
263                 if ($this->get_tag_id($tag) != false) {
264                         return true;
265                 }
266                 else {
267                         return false;
268                 }
269         }
270
271         /**
272          * Creates a new tag (noramlized) in the database, returns the ID of the
273          * created tag or false
274          *
275          * @uses phptagengine::normalize() to normalize the $tag
276          * @uses phptagengine::tag_exists() to see if the tag needs creating
277          *
278          * @param string $tag
279          * @return mixed
280          */
281         function create_tag($tag = null) {
282                 if ($tag == null) {
283                         return false;
284                 }
285                 $tag = $this->normalize($tag);
286                 if (strlen($tag) < 1) {
287                         return false;
288                 }
289                 $test = $this->get_tag_id($tag);
290                 if ($test != false) {
291                         return $test;
292                 }
293                 $result = $this->db->Execute("
294                         INSERT
295                         INTO $this->table_tag_names
296                         ( ".$this->db_col_escape_char."name".$this->db_col_escape_char."
297                         )
298                         VALUES
299                         ( ".$this->db->qstr($tag)."                     
300                         )
301                 ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
302                
303                 if ($result) {
304                         $id = false;
305                         if (strstr($this->db->databaseType, 'postgres')) {
306                                 $id = $this->db->GetOne("
307                                         SELECT CURRVAL('".$this->table_tag_names."_id_seq') as id
308                                 ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
309                         }
310                         else {
311                                 $id = $this->db->Insert_ID();
312                         }
313                         return $id;
314                 }
315                
316                 return false;
317         }
318
319         /**
320          * Adds a tag to an item, creates the tag if needed
321          *
322          * @uses phptagengine::default_value() to set a default value for $type and $user if needed
323          * @uses phptagengine::create_tag() to create a new tag or get the id of the existing tag
324          *
325          * @param string $user user creating the tag
326          * @param string $item item being tagged
327          * @param string $tag tag being used
328          * @param string $type type of item being tagged
329          * @return boolean
330          */
331         function add_tag($user = null, $item = null, $tag = null, $type = null) {
332                 if ($item == null || $tag == null) {
333                         return false;
334                 }
335                 $type = $this->default_value('type', $type);
336                 $user = $this->default_value('user', $user);
337                 $tag_id = $this->create_tag($tag);
338                 if ($tag_id == false) {
339                         return false;
340                 }
341                 if ($this->item_tag_exists($user, $item, $tag, $type)) {
342                         return true;
343                 }
344                 else {
345                         $this->set_db_col_escape_char();
346                         $result = $this->db->Execute("
347                                 INSERT
348                                 INTO $this->table_tags
349                                 ( ".$this->db_col_escape_char."user".$this->db_col_escape_char."
350                                 , ".$this->db_col_escape_char."item".$this->db_col_escape_char."
351                                 , ".$this->db_col_escape_char."type".$this->db_col_escape_char."
352                                 , ".$this->db_col_escape_char."tag".$this->db_col_escape_char."
353                                 , ".$this->db_col_escape_char."date".$this->db_col_escape_char."
354                                 )
355                                 VALUES
356                                 ( ".$this->db->qstr($user)."
357                                 , ".$this->db->qstr($item)."
358                                 , ".$this->db->qstr($type)."
359                                 , ".$this->db->qstr($tag_id)."
360                                 , ".$this->db->DBDate(date("Y-m-d H:i:s"))."
361                                 )
362                         ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
363                         if ($result) {
364                                 return true;
365                         }
366                 }
367                 return false;
368         }
369
370         /**
371          * Save item tags
372          *
373          * @param string $item
374          * @param string $tags
375          * @param string $type
376          * @param string $user
377          *
378          * @return array key success is 'y/n', key tags is an arra of the item's tags
379          */
380         function save_tags($item, $tags, $type = null, $user = null) {
381                 $type = $this->default_value('type', $type);
382                 $user = $this->default_value('user', $user);
383                 $old_tags = $this->get_tags($item, $type, $user);
384                 $new_tags = array();
385                 $success = 'y';
386                 if ($tags != null && $tags != '') {
387                         $tags = explode(' ', $tags);
388                         $tags = array_unique($tags);
389                         natcasesort($tags);
390                         reset($tags);
391                         if (count($tags) > 0) {
392                                 foreach ($tags as $tag) {
393                                         if ($tag != '') {
394                                                 if ($this->add_tag($user, $item, $tag, $type)) {
395                                                         $new_tags[] = $this->normalize($tag);
396                                                 }
397                                                 else {
398                                                         $success = 'n';
399                                                 }
400                                         }
401                                 }
402                                 if (count($new_tags) > 0) {
403                                         $tags = implode(' ', $new_tags);
404                                 }
405                         }
406                 }
407                 if (count($old_tags) > 0) {
408                         foreach ($old_tags as $id => $tag) {
409                                 if (!in_array($tag, $new_tags)) {
410                                         if (!$this->remove_tag_by_id(str_replace('id_', '', $id))) {
411                                                 $success = 'n';
412                                         }
413                                 }
414                         }
415                 }
416                 $result = array(
417                         'success' => $success
418                         , 'tags' => $new_tags
419                 );
420                 return $result;
421         }
422        
423
424         /**
425          * Removes a tag
426          *
427          * @uses phptagengine::get_item_tag_id()
428          * @uses phptagengine::remove_tag_by_id()
429          *
430          * @param string $item
431          * @param string $tag
432          * @param string $user
433          * @param string $type
434          * @return boolean
435          */
436         function remove_tag($item = null, $tag = null, $user = null, $type = null) {
437                 $id = $this->get_item_tag_id($user, $item, $tag, $type);
438                 return $this->remove_tag_by_id($id);
439         }
440
441         /**
442          * Removes a tag by id
443          *
444          * @param integer $id
445          * @return boolean
446          */
447         function remove_tag_by_id($id) {
448                 if ($id == null) {
449                         return false;
450                 }
451                 $result = $this->db->Execute("
452                         DELETE
453                         FROM $this->table_tags
454                         WHERE id = ".$this->db->qstr($id)."
455                 ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
456                 if ($result) {
457                         return true;
458                 }
459                 return false;
460         }
461        
462         /**
463          * Gets the tags for an item
464          *
465          * @param string $item
466          * @param string $type
467          * @param string $user
468          * @param boolean $use_cache
469          * @return array
470          */
471         function get_tags($item = null, $type = null, $user = null, $use_cache = false) {
472                 if ($item == null) {
473                         return false;
474                 }
475                 $tags = array();
476                 if ($use_cache) {
477                         if (isset($this->item_tags_cache['item_'.$item])) {
478                                 $tags = $this->item_tags_cache['item_'.$item];
479                         }
480                         return $tags;
481                 }
482                 $where = '';
483                 if ($user != null) {
484                         $where .= ' AND t.user = '.$this->db->qstr($user);
485                 }
486                 if ($item != null) {
487                         $where .= ' AND t.item = '.$this->db->qstr($item);
488                 }
489                 if ($type != null) {
490                         $where .= ' AND t.type = '.$this->db->qstr($type);
491                 }
492                 if ($where == '') {
493                         return false;
494                 }
495                 $result = $this->db->Execute("
496                         SELECT t.id AS ID
497                         , tn.name AS NAME
498                         FROM $this->table_tags t
499                         JOIN $this->table_tag_names tn
500                         ON t.tag = tn.id
501                         WHERE 1 = 1
502                         $where
503                         ORDER BY tn.name
504                 ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
505                 if ($result && $result->RowCount() > 0) {
506                         while ($data = $result->FetchNextObject()) {
507                                 $tags['id_'.$data->ID] = $data->NAME;
508                         }
509                 }
510                 return $tags;
511         }
512        
513         /**
514          * Completely removes a tag from the system (from @link phptagengine::$table_tags and @link phptagengine::$table_tag_names)
515          *
516          * @param string $tag
517          * @return boolean
518          */
519         function delete_tag($tag = null) {
520                 if (is_null($tag) || strstr($tag, ' ')) {
521                         return false;
522                 }
523                 $tag_id = $this->get_tag_id($tag);
524                 if ($tag_id == false) {
525                         return false;
526                 }
527                 $result = $this->db->Execute("
528                         DELETE
529                         FROM $this->table_tags
530                         WHERE tag = '$tag_id'
531                 ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
532                 if (!$result) {
533                         return false;
534                 }
535                 $result = $this->db->Execute("
536                         DELETE
537                         FROM $this->table_tag_names
538                         WHERE id = '$tag_id'
539                 ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
540                 if (!$result) {
541                         return false;
542                 }
543                 return true;
544         }
545        
546         /**
547          * Changes the name of a tag, or consolidates two existing tags
548          *
549          * @param string $old_tag
550          * @param string $new_tag
551          * @return boolean
552          */
553         function edit_tag($old_tag = null, $new_tag = null) {
554                 if (is_null($old_tag) || is_null($new_tag) || strstr($new_tag