root/tags/1.0/phptagengine.class.inc.php

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

creating 1.0 and stable tags

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         /**
180          * Initializes the class
181          *
182          * @return phptagengine
183          */
184         function phptagengine() {
185                 $this->base_url = 'http://example.com/';
186                 $this->ajax_handler = 'http://example.com/ajax.php';
187                 $this->language = 'english';
188                 $this->charset = 'UTF-8';
189                 $this->db_type = 'mysql';
190                 $this->strings = array();
191                 $this->tag_browse_url = 'http://example.com/index.php?view=tags&tag=<tag>&type=<type>';
192                 $this->default_type = '';
193                 $this->default_user = 1;
194                 $this->edit_button_display = 'text';
195                 $this->delete_button_display = 'text';
196                 $this->yac = true;
197                 $this->yac_files = array(
198                         'yahoo.js'
199                         ,'dom.js'
200                         ,'event.js'
201                         ,'autocomplete.js'
202                 );
203                 $this->version = '1.0';
204         }
205        
206         /**
207          * Sets the character used for escaping column names for the database type in use
208          */
209         function set_db_col_escape_char() {
210                 switch ($this->db->databaseType) {
211                         case 'mysql':
212                                 $this->db_col_escape_char = '`';
213                                 break;
214                         case 'postgres7':
215                         case 'mssql':
216                                 $this->db_col_escape_char = '"';
217                                 break;
218                 }
219         }
220
221         /**
222          * Sets the type or user property to the default value if the value is
223          * null, could be extended in the future
224          * @param string $prop expected 'type' or 'user'
225          * @param mixed $value if this is null, we set the default
226          */
227         function default_value($prop, $value) {
228                 if ($value == null && in_array($prop, array('type', 'user'))) {
229                         eval('$value = $this->default_'.$prop.';');
230                 }
231                 return $value;
232         }
233        
234         /**
235          * Set a tag to lowercase and remove spaces, could be extended in the future
236          * @param mixed $value the value to be normalized
237          * @param string $type the type of value, used for the switch statement
238          * @return mixed
239          */
240         function normalize($value, $type = 'tag') {
241                 switch ($type) {
242                         case 'tag':
243                                 $value = preg_replace('|[^a-z0-9_.\-@#$%*!&]|i', '', strtolower($value));
244                                 break;
245                 }
246                 return $value;
247         }
248
249         /**
250          * Does a tag already exist
251          *
252          * @uses phptagengine::get_tag_id()
253          *
254          * @param string $tag
255          * @return boolean
256          */
257         function tag_exists($tag) {
258                 if ($this->get_tag_id($tag) != false) {
259                         return true;
260                 }
261                 else {
262                         return false;
263                 }
264         }
265
266         /**
267          * Creates a new tag (noramlized) in the database, returns the ID of the
268          * created tag or false
269          *
270          * @uses phptagengine::normalize() to normalize the $tag
271          * @uses phptagengine::tag_exists() to see if the tag needs creating
272          *
273          * @param string $tag
274          * @return mixed
275          */
276         function create_tag($tag = null) {
277                 if ($tag == null) {
278                         return false;
279                 }
280                 $tag = $this->normalize($tag);
281                 if (strlen($tag) < 1) {
282                         return false;
283                 }
284                 $test = $this->get_tag_id($tag);
285                 if ($test != false) {
286                         return $test;
287                 }
288                 $result = $this->db->Execute("
289                         INSERT
290                         INTO $this->table_tag_names
291                         ( ".$this->db_col_escape_char."name".$this->db_col_escape_char."
292                         )
293                         VALUES
294                         ( ".$this->db->qstr($tag)."                     
295                         )
296                 ") or die($this->db->ErrorMsg().' in '.__FILE__);
297                
298                 if ($result) {
299                         $id = false;
300                         if (strstr($this->db->databaseType, 'postgres')) {
301                                 $id = $this->db->GetOne("
302                                         SELECT CURRVAL('".$this->table_tag_names."_id_seq') as id
303                                 ") or die($this->db->ErrorMsg());
304                         }
305                         else {
306                                 $id = $this->db->Insert_ID();
307                         }
308                         return $id;
309                 }
310                
311                 return false;
312         }
313
314         /**
315          * Adds a tag to an item, creates the tag if needed
316          *
317          * @uses phptagengine::default_value() to set a default value for $type and $user if needed
318          * @uses phptagengine::create_tag() to create a new tag or get the id of the existing tag
319          *
320          * @param string $user user creating the tag
321          * @param string $item item being tagged
322          * @param string $tag tag being used
323          * @param string $type type of item being tagged
324          * @return boolean
325          */
326         function add_tag($user = null, $item = null, $tag = null, $type = null) {
327                 if ($item == null || $tag == null) {
328                         return false;
329                 }
330                 $type = $this->default_value('type', $type);
331                 $user = $this->default_value('user', $user);
332                 $tag_id = $this->create_tag($tag);
333                 if ($tag_id == false) {
334                         return false;
335                 }
336                 if ($this->item_tag_exists($user, $item, $tag, $type)) {
337                         return true;
338                 }
339                 else {
340                         $this->set_db_col_escape_char();
341                         $result = $this->db->Execute("
342                                 INSERT
343                                 INTO $this->table_tags
344                                 ( ".$this->db_col_escape_char."user".$this->db_col_escape_char."
345                                 , ".$this->db_col_escape_char."item".$this->db_col_escape_char."
346                                 , ".$this->db_col_escape_char."type".$this->db_col_escape_char."
347                                 , ".$this->db_col_escape_char."tag".$this->db_col_escape_char."
348                                 , ".$this->db_col_escape_char."date".$this->db_col_escape_char."
349                                 )
350                                 VALUES
351                                 ( ".$this->db->qstr($user)."
352                                 , ".$this->db->qstr($item)."
353                                 , ".$this->db->qstr($type)."
354                                 , ".$this->db->qstr($tag_id)."
355                                 , ".$this->db->DBDate(date("Y-m-d H:i:s"))."
356                                 )
357                         ") or die($this->db->ErrorMsg().' in '.__FILE__);
358                         if ($result) {
359                                 return true;
360                         }
361                 }
362                 return false;
363         }
364
365         /**
366          * Save item tags
367          *
368          * @param string $item
369          * @param string $tags
370          * @param string $type
371          * @param string $user
372          *
373          * @return array key success is 'y/n', key tags is an arra of the item's tags
374          */
375         function save_tags($item, $tags, $type = null, $user = null) {
376                 $type = $this->default_value('type', $type);
377                 $user = $this->default_value('user', $user);
378                 $old_tags = $this->get_tags($item, $type, $user);
379                 $new_tags = array();
380                 $success = 'y';
381                 if ($tags != null && $tags != '') {
382                         $tags = explode(' ', $tags);
383                         $tags = array_unique($tags);
384                         natcasesort($tags);
385                         reset($tags);
386                         if (count($tags) > 0) {
387                                 foreach ($tags as $tag) {
388                                         if ($tag != '') {
389                                                 if ($this->add_tag($user, $item, $tag, $type)) {
390                                                         $new_tags[] = $this->normalize($tag);
391                                                 }
392                                                 else {
393                                                         $success = 'n';
394                                                 }
395                                         }
396                                 }
397                                 if (count($new_tags) > 0) {
398                                         $tags = implode(' ', $new_tags);
399                                 }
400                         }
401                 }
402                 if (count($old_tags) > 0) {
403                         foreach ($old_tags as $id => $tag) {
404                                 if (!in_array($tag, $new_tags)) {
405                                         if (!$this->remove_tag_by_id(str_replace('id_', '', $id))) {
406                                                 $success = 'n';
407                                         }
408                                 }
409                         }
410                 }
411                 $result = array(
412                         'success' => $success
413                         , 'tags' => $new_tags
414                 );
415                 return $result;
416         }
417        
418
419         /**
420          * Removes a tag
421          *
422          * @uses phptagengine::get_item_tag_id()
423          * @uses phptagengine::remove_tag_by_id()
424          *
425          * @param string $item
426          * @param string $tag
427          * @param string $user
428          * @param string $type
429          * @return boolean
430          */
431         function remove_tag($item = null, $tag = null, $user = null, $type = null) {
432                 $id = $this->get_item_tag_id($user, $item, $tag, $type);
433                 return $this->remove_tag_by_id($id);
434         }
435
436         /**
437          * Removes a tag by id
438          *
439          * @param integer $id
440          * @return boolean
441          */
442         function remove_tag_by_id($id) {
443                 if ($id == null) {
444                         return false;
445                 }
446                 $result = $this->db->Execute("
447                         DELETE
448                         FROM $this->table_tags
449                         WHERE id = ".$this->db->qstr($id)."
450                 ") or die($this->db->ErrorMsg().' in '.__FILE__);
451                 if ($result) {
452                         return true;
453                 }
454                 return false;
455         }
456        
457         /**
458          * Gets the tags for an item
459          *
460          * @param string $item
461          * @param string $type
462          * @param string $user
463          * @param boolean $use_cache
464          * @return array
465          */
466         function get_tags($item = null, $type = null, $user = null, $use_cache = false) {
467                 if ($item == null) {
468                         return false;
469                 }
470                 $tags = array();
471                 if ($use_cache) {
472                         if (isset($this->item_tags_cache['item_'.$item])) {
473                                 $tags = $this->item_tags_cache['item_'.$item];
474                         }
475                         return $tags;
476                 }
477                 $where = '';
478                 if ($user != null) {
479                         $where .= ' AND t.user = '.$this->db->qstr($user);
480                 }
481                 if ($item != null) {
482                         $where .= ' AND t.item = '.$this->db->qstr($item);
483                 }
484                 if ($type != null) {
485                         $where .= ' AND t.type = '.$this->db->qstr($type);
486                 }
487                 if ($where == '') {
488                         return false;
489                 }
490                 $result = $this->db->Execute("
491                         SELECT t.id AS ID
492                         , tn.name AS NAME
493                         FROM $this->table_tags t
494                         JOIN $this->table_tag_names tn
495                         ON t.tag = tn.id
496                         WHERE 1 = 1
497                         $where
498                         ORDER BY tn.name
499                 ") or die($this->db->ErrorMsg().' in '.__FILE__);
500                 if ($result && $result->RowCount() > 0) {
501                         while ($data = $result->FetchNextObject()) {
502                                 $tags['id_'.$data->ID] = $data->NAME;
503                         }
504                 }
505                 return $tags;
506         }
507        
508         /**
509          * Completely removes a tag from the system (from @link phptagengine::$table_tags and @link phptagengine::$table_tag_names)
510          *
511          * @param string $tag
512          * @return boolean
513          */
514         function delete_tag($tag = null) {
515                 if (is_null($tag) || strstr($tag, ' ')) {
516                         return false;
517                 }
518                 $tag_id = $this->get_tag_id($tag);
519                 if ($tag_id == false) {
520                         return false;
521                 }
522                 $result = $this->db->Execute("
523                         DELETE
524                         FROM $this->table_tags
525                         WHERE tag = '$tag_id'
526                 ") or die($this->db->ErrorMsg().' in '.__FILE__);
527                 if (!$result) {
528                         return false;
529                 }
530                 $result = $this->db->Execute("
531                         DELETE
532                         FROM $this->table_tag_names
533                         WHERE id = '$tag_id'
534                 ") or die($this->db->ErrorMsg().' in '.__FILE__);
535                 if (!$result) {
536                         return false;
537                 }
538                 return true;
539         }
540        
541         /**
542          * Changes the name of a tag, or consolidates two existing tags
543          *
544          * @param string $old_tag
545          * @param string $new_tag
546          * @return boolean
547          */
548         function edit_tag($old_tag = null, $new_tag = null) {
549                 if (is_null($old_tag) || is_null($new_tag) || strstr($new_tag, ' ')) {
550                         return false;
551                 }
552                 if ($old_tag == $new_tag