remove the rows x cols logic
[phpfspot.git] / phpfspot.class.php
1 <?php
2
3 /***************************************************************************
4  *
5  * Copyright (c) by Andreas Unterkircher, unki@netshadow.at
6  * All rights reserved
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  ***************************************************************************/
23
24 require_once "phpfspot_cfg.php";
25 require_once "phpfspot_db.php";
26
27 class PHPFSPOT {
28
29    var $cfg;
30    var $db;
31    var $cfg_db;
32    var $tmpl;
33    var $tags;
34    var $avail_tags;
35    private $dbver;
36
37    /**
38     * class constructor
39     *
40     * this function will be called on class construct
41     * and will check requirements, loads configuration,
42     * open databases and start the user session
43     */
44    public function __construct()
45    {
46       $this->cfg = new PHPFSPOT_CFG;
47
48       /* set application name and version information */
49       $this->cfg->product = "phpfspot";
50       $this->cfg->version = "1.2";
51
52       $this->sort_orders= array(
53          'date_asc' => 'Date &uarr;',
54          'date_desc' => 'Date &darr;',
55          'name_asc' => 'Name &uarr;',
56          'name_desc' => 'Name &darr;'
57       );
58
59       /* Check necessary requirements */
60       if(!$this->checkRequirements()) {
61          exit(1);
62       }
63
64       $this->db  = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
65       if(!is_writeable($this->cfg->fspot_db)) {
66          print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
67          exit(1);
68       }
69
70       $this->dbver = $this->getFspotDBVersion();
71
72       if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
73          print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
74          exit(1);
75       }
76
77       if(!is_writeable($this->cfg->base_path ."/templates_c")) {
78          print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
79          exit(1);
80       }
81
82       $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
83       if(!is_writeable($this->cfg->phpfspot_db)) {
84          print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
85          exit(1);
86       }
87
88       $this->check_config_table();
89
90       /* include Smarty template engine */
91       if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
92          exit(1);
93       }
94       require $this->cfg->smarty_path .'/libs/Smarty.class.php';
95       /* overload Smarty class if our own template handler */
96       require_once "phpfspot_tmpl.php";
97       $this->tmpl = new PHPFSPOT_TMPL($this);
98
99       /* check if all necessary indices exist */
100       $this->checkDbIndices();
101
102       /* if session is not yet started, do it now */
103       if(session_id() == "")
104          session_start();
105
106       if(!isset($_SESSION['tag_condition']))
107          $_SESSION['tag_condition'] = 'or';
108
109       if(!isset($_SESSION['sort_order']))
110          $_SESSION['sort_order'] = 'date_asc';
111
112       if(!isset($_SESSION['searchfor']))
113          $_SESSION['searchfor'] = '';
114
115       // if begin_with is still set but thumbs_per_page is now 0, unset it
116       if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
117          unset($_SESSION['begin_with']);
118
119    } // __construct()
120
121    public function __destruct()
122    {
123
124    } // __destruct()
125
126    /**
127     * show - generate html output
128     *
129     * this function can be called after the constructor has
130     * prepared everyhing. it will load the index.tpl smarty
131     * template. if necessary it will registere pre-selects
132     * (photo index, photo, tag search, date search) into
133     * users session.
134     */
135    public function show()
136    {
137       $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
138       $this->tmpl->assign('page_title', $this->cfg->page_title);
139       $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
140       $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
141
142       if(isset($_GET['mode'])) {
143
144          $_SESSION['start_action'] = $_GET['mode'];
145
146          switch($_GET['mode']) {
147             case 'showpi':
148                if(isset($_GET['tags'])) {
149                   $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
150                }
151                if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
152                   $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
153                }
154                if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
155                   $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
156                }
157                break;
158             case 'showp':
159                if(isset($_GET['tags'])) {
160                   $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
161                   $_SESSION['start_action'] = 'showp';
162                }
163                if(isset($_GET['id']) && is_numeric($_GET['id'])) {
164                   $_SESSION['current_photo'] = $_GET['id'];
165                   $_SESSION['start_action'] = 'showp';
166                }
167                if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
168                   $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
169                }
170                if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
171                   $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
172                }
173                break;
174             case 'export':
175                $this->tmpl->show("export.tpl");
176                return;
177                break;
178             case 'slideshow':
179                $this->tmpl->show("slideshow.tpl");
180                return;
181                break;
182             case 'rss':
183                if(isset($_GET['tags'])) {
184                   $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
185                }
186                if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
187                   $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
188                }
189                if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
190                   $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
191                }
192                $this->getRSSFeed();
193                return;
194                break;
195          }
196       }
197
198       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
199          $this->tmpl->assign('date_search_enabled', true);
200
201       $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
202       $this->tmpl->assign('from_date', $this->get_calendar('from'));
203       $this->tmpl->assign('to_date', $this->get_calendar('to'));
204       $this->tmpl->assign('content_page', 'welcome.tpl');
205       $this->tmpl->show("index.tpl");
206
207    } // show()
208
209    /**
210     * get_tags - grab all tags of f-spot's database
211     *
212     * this function will get all available tags from
213     * the f-spot database and store them within two
214     * arrays within this class for later usage. in
215     * fact, if the user requests (hide_tags) it will
216     * opt-out some of them.
217     *
218     * this function is getting called once by show()
219     */
220    private function get_tags()
221    {
222       $this->avail_tags = Array();
223       $count = 0;
224    
225       if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
226          $query_str="
227             SELECT
228                DISTINCT t1.id as id, t1.name as name
229             FROM  
230                photo_tags pt1
231             INNER JOIN photo_tags
232                pt2 ON pt1.photo_id=pt2.photo_id
233             INNER JOIN tags t1
234                ON t1.id=pt1.tag_id
235             INNER JOIN tags t2
236                ON t2.id=pt2.tag_id
237             WHERE
238                t2.name IN  ('".implode("','",$this->cfg->show_tags)."')
239             ORDER BY
240                t1.sort_priority ASC";
241
242          $result = $this->db->db_query($query_str);
243       }
244       else
245       {
246          $result = $this->db->db_query("
247             SELECT id,name
248             FROM tags
249             ORDER BY sort_priority ASC
250          ");
251       }
252       
253       while($row = $this->db->db_fetch_object($result)) {
254
255          $tag_id = $row['id'];
256          $tag_name = $row['name'];
257
258          /* if the user has specified to ignore this tag in phpfspot's
259             configuration, ignore it here so it does not get added to
260             the tag list.
261          */
262          if(in_array($row['name'], $this->cfg->hide_tags))
263             continue;
264
265          /* if you include the following if-clause and the user has specified
266             to only show certain tags which are specified in phpfspot's
267             configuration, ignore all others so they will not be added to the
268             tag list.
269          if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
270             !in_array($row['name'], $this->cfg->show_tags))
271             continue;
272          */
273
274          $this->tags[$tag_id] = $tag_name; 
275          $this->avail_tags[$count] = $tag_id;
276          $count++;
277
278       }
279
280    } // get_tags()
281
282    /** 
283     * extract all photo details
284     * 
285     * retrieve all available details from f-spot's
286     * database and return them as object
287     */
288    public function get_photo_details($idx)
289    {
290       if($this->dbver < 9) {
291          $query_str = "
292             SELECT p.id, p.name, p.time, p.directory_path, p.description
293             FROM photos p
294          ";
295       }
296       else {
297          $query_str = "
298             SELECT p.id, p.uri, p.time, p.description
299             FROM photos p
300          ";
301       }
302
303       /* if show_tags is set, only return details for photos which
304          are specified to be shown
305       */
306       if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
307          $query_str.= "
308             INNER JOIN photo_tags pt
309                ON p.id=pt.photo_id
310             INNER JOIN tags t
311                ON pt.tag_id=t.id
312             WHERE p.id='". $idx ."'
313             AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
314       }
315       else {
316          $query_str.= "
317             WHERE p.id='". $idx ."'
318          ";
319       }
320
321       if($result = $this->db->db_query($query_str)) {
322
323          $row = $this->db->db_fetch_object($result);
324
325          if($this->dbver < 9) {
326             $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
327          }
328
329          return $row;
330
331       }
332    
333       return null;
334
335    } // get_photo_details
336
337    /**
338     * returns aligned photo names 
339     *
340     * this function returns aligned (length) names for
341     * an specific photo. If the length of the name exceeds
342     * $limit the name will be shrinked (...)
343     */
344    public function getPhotoName($idx, $limit = 0)
345    {
346       if($details = $this->get_photo_details($idx)) {
347          if($long_name = $this->parse_uri($details['uri'], 'filename')) {
348             $name = $this->shrink_text($long_name, $limit);
349             return $name;
350          }
351       }
352
353       return null;
354
355    } // getPhotoName()
356
357    /**
358     * shrink text according provided limit
359     *
360     * If the length of the name exceeds $limit the
361     * text will be shortend and some content in between
362     * will be replaced with "..." 
363     */
364    private function shrink_text($text, $limit)
365    {
366       if($limit != 0 && strlen($text) > $limit) {
367          $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
368       }
369
370       return $text;
371
372    } // shrink_text();
373
374    /**
375     * translate f-spoth photo path
376     * 
377     * as the full-qualified path recorded in the f-spot database
378     * is usally not the same as on the webserver, this function
379     * will replace the path with that one specified in the cfg
380     */
381    public function translate_path($path, $width = 0)
382    {  
383       return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
384
385    } // translate_path
386
387    /**
388     * control HTML ouput for a single photo
389     *
390     * this function provides all the necessary information
391     * for the single photo template.
392     */
393    public function showPhoto($photo)
394    {
395       /* get all photos from the current photo selection */
396       $all_photos = $this->getPhotoSelection();
397       $count = count($all_photos);
398
399       for($i = 0; $i < $count; $i++) {
400          
401          // $get_next will be set, when the photo which has to
402          // be displayed has been found - this means that the
403          // next available is in fact the NEXT image (for the
404          // navigation icons) 
405          if(isset($get_next)) {
406             $next_img = $all_photos[$i];
407             break;
408          }
409
410          /* the next photo is our NEXT photo */
411          if($all_photos[$i] == $photo) {
412             $get_next = 1;
413          }
414          else {
415             $previous_img = $all_photos[$i];
416          }
417
418          if($photo == $all_photos[$i]) {
419                $current = $i;
420          }
421       }
422
423       $details = $this->get_photo_details($photo);
424
425       if(!$details) {
426          print "error";
427          return;
428       }
429
430       $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
431       $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
432
433       if(!file_exists($orig_path)) {
434          $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
435          return;
436       }
437
438       if(!is_readable($orig_path)) {
439          $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
440          return;
441       }
442
443       /* If the thumbnail doesn't exist yet, try to create it */
444       if(!file_exists($thumb_path)) {
445          $this->gen_thumb($photo, true);
446          $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
447       }
448
449       /* get f-spot database meta information */
450       $meta = $this->get_meta_informations($orig_path);
451
452       /* If EXIF data are available, use them */
453       if(isset($meta['ExifImageWidth'])) {
454          $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
455       } else {
456          $info = getimagesize($orig_path);
457          $meta_res = $info[0] ."x". $info[1]; 
458       }
459
460       $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
461       $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
462       $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
463
464       $extern_link = "index.php?mode=showp&id=". $photo;
465       $current_tags = $this->getCurrentTags();
466       if($current_tags != "") {
467          $extern_link.= "&tags=". $current_tags;
468       }
469       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
470          $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
471       }
472
473       $this->tmpl->assign('extern_link', $extern_link);
474
475       if(!file_exists($thumb_path)) {
476          $this->_error("Can't open file ". $thumb_path ."\n");
477          return;
478       }
479
480       $info = getimagesize($thumb_path);
481
482       $this->tmpl->assign('description', $details['description']);
483       $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
484
485       $this->tmpl->assign('width', $info[0]);
486       $this->tmpl->assign('height', $info[1]);
487       $this->tmpl->assign('ExifMadeOn', $meta_date);
488       $this->tmpl->assign('ExifMadeWith', $meta_make);
489       $this->tmpl->assign('ExifOrigResolution', $meta_res);
490       $this->tmpl->assign('ExifFileSize', $meta_size);
491  
492       $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&amp;width=". $this->cfg->photo_width);
493       $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
494       $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
495
496       $this->tmpl->assign('tags', $this->get_photo_tags($photo));
497       $this->tmpl->assign('current', $current);
498
499       if($previous_img) {
500          $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
501          $this->tmpl->assign('prev_img', $previous_img);
502       }
503
504       if($next_img) {
505          $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
506          $this->tmpl->assign('next_img', $next_img);
507       }
508       $this->tmpl->assign('mini_width', $this->cfg->mini_width);
509       $this->tmpl->assign('photo_number', $i);
510       $this->tmpl->assign('photo_count', count($all_photos));
511
512       $this->tmpl->show("single_photo.tpl");
513
514    } // showPhoto()
515
516    /**
517     * all available tags and tag cloud
518     *
519     * this function outputs all available tags (time ordered)
520     * and in addition output them as tag cloud (tags which have
521     * many photos will appears more then others)
522     */
523    public function getAvailableTags()
524    {
525       /* retrive tags from database */
526       $this->get_tags();
527
528       $output = "";
529
530       $result = $this->db->db_query("
531          SELECT tag_id as id, count(tag_id) as quantity
532          FROM photo_tags
533          INNER JOIN tags t
534             ON t.id = tag_id
535          GROUP BY tag_id
536          ORDER BY t.name ASC
537       ");
538
539       $tags = Array();
540
541       while($row = $this->db->db_fetch_object($result)) {
542          $tags[$row['id']] = $row['quantity'];
543       }
544
545       // change these font sizes if you will
546       $max_size = 125; // max font size in %
547       $min_size = 75; // min font size in %
548
549       // get the largest and smallest array values
550       $max_qty = max(array_values($tags));
551       $min_qty = min(array_values($tags));
552
553       // find the range of values
554       $spread = $max_qty - $min_qty;
555       if (0 == $spread) { // we don't want to divide by zero
556          $spread = 1;
557       }
558
559       // determine the font-size increment
560       // this is the increase per tag quantity (times used)
561       $step = ($max_size - $min_size)/($spread);
562
563       // loop through our tag array
564       foreach ($tags as $key => $value) {
565
566          if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
567             continue;
568
569          // calculate CSS font-size
570          // find the $value in excess of $min_qty
571          // multiply by the font-size increment ($size)
572          // and add the $min_size set above
573          $size = $min_size + (($value - $min_qty) * $step);
574           // uncomment if you want sizes in whole %:
575          $size = ceil($size);
576
577          if(isset($this->tags[$key])) {
578             $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
579          }
580
581       }
582
583       $output = substr($output, 0, strlen($output)-2);
584       print $output;
585
586    } // getAvailableTags()
587
588    /**
589     * output all selected tags
590     *
591     * this function output all tags which have been selected
592     * by the user. the selected tags are stored in the 
593     * session-variable $_SESSION['selected_tags']
594     */
595    public function getSelectedTags()
596    {
597       /* retrive tags from database */
598       $this->get_tags();
599
600       $output = "";
601
602       foreach($this->avail_tags as $tag)
603       {
604          // return all selected tags
605          if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
606             $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
607          }
608       }
609
610       if($output != "") {
611          $output = substr($output, 0, strlen($output)-2);
612          return $output;
613       }
614       else {
615          return "no tags selected";
616       }
617
618    } // getSelectedTags()
619
620    /**
621     * add tag to users session variable
622     *
623     * this function will add the specified to users current
624     * tag selection. if a date search has been made before
625     * it will be now cleared
626     */
627    public function addTag($tag)
628    {
629       if(!isset($_SESSION['selected_tags']))
630          $_SESSION['selected_tags'] = Array();
631
632       if(isset($_SESSION['searchfor']))
633          unset($_SESSION['searchfor']);
634
635       if(!in_array($tag, $_SESSION['selected_tags']))
636          array_push($_SESSION['selected_tags'], $tag);
637    
638    } // addTag()
639
640    /**
641     * remove tag to users session variable
642     *
643     * this function removes the specified tag from
644     * users current tag selection
645     */
646    public function delTag($tag)
647    {
648       if(isset($_SESSION['searchfor']))
649          unset($_SESSION['searchfor']);
650
651       if(isset($_SESSION['selected_tags'])) {
652          $key = array_search($tag, $_SESSION['selected_tags']);
653          unset($_SESSION['selected_tags'][$key]);
654          sort($_SESSION['selected_tags']);
655       }
656
657    } // delTag()
658
659    /**
660     * reset tag selection
661     *
662     * if there is any tag selection, it will be
663     * deleted now
664     */
665    public function resetTags()
666    {
667       if(isset($_SESSION['selected_tags']))
668          unset($_SESSION['selected_tags']);
669
670    } // resetTags()
671
672    /**
673     * reset single photo
674     *
675     * if a specific photo was requested (external link)
676     * unset the session variable now
677     */
678    public function resetPhotoView()
679    {
680       if(isset($_SESSION['current_photo']))
681          unset($_SESSION['current_photo']);
682
683    } // resetPhotoView();
684
685    /**
686     * reset tag search
687     *
688     * if any tag search has taken place, reset
689     * it now
690     */
691    public function resetTagSearch()
692    {
693       if(isset($_SESSION['searchfor']))
694          unset($_SESSION['searchfor']);
695
696    } // resetTagSearch()
697
698     /**
699     * reset date search
700     *
701     * if any date search has taken place, reset
702     * it now
703     */
704    public function resetDateSearch()
705    {
706       if(isset($_SESSION['from_date']))
707          unset($_SESSION['from_date']);
708       if(isset($_SESSION['to_date']))
709          unset($_SESSION['to_date']);
710
711    } // resetDateSearch();
712
713    /**
714     * return all photo according selection
715     *
716     * this function returns all photos based on
717     * the tag-selection, tag- or date-search.
718     * the tag-search also has to take care of AND
719     * and OR conjunctions
720     */
721    public function getPhotoSelection()
722    {  
723       $matched_photos = Array();
724
725       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
726          $from_date = $_SESSION['from_date'];
727          $to_date = $_SESSION['to_date'];
728          $additional_where_cond = "
729                p.time>='". $from_date ."'
730             AND
731                p.time<='". $to_date ."'
732          ";
733       } 
734
735       if(isset($_SESSION['sort_order'])) {
736          $order_str = $this->get_sort_order();
737       }
738
739       /* return a search result */
740       if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
741          $query_str = "
742             SELECT DISTINCT pt1.photo_id
743                FROM photo_tags pt1
744             INNER JOIN photo_tags pt2
745                ON pt1.photo_id=pt2.photo_id
746             INNER JOIN tags t1
747                ON pt1.tag_id=t1.id
748             INNER JOIN photos p
749                ON pt1.photo_id=p.id
750             INNER JOIN tags t2
751                ON pt2.tag_id=t2.id
752             WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
753
754          if(isset($additional_where_cond))
755             $query_str.= "AND ". $additional_where_cond ." ";
756
757          if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
758             $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
759          }
760          
761          if(isset($order_str))
762             $query_str.= $order_str;
763
764          $result = $this->db->db_query($query_str);
765          while($row = $this->db->db_fetch_object($result)) {
766             array_push($matched_photos, $row['photo_id']);
767          }
768          return $matched_photos;
769       }
770
771       /* return according the selected tags */
772       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
773          $selected = "";
774          foreach($_SESSION['selected_tags'] as $tag)
775             $selected.= $tag .",";
776          $selected = substr($selected, 0, strlen($selected)-1);
777
778          /* photo has to match at least on of the selected tags */
779          if($_SESSION['tag_condition'] == 'or') {
780             $query_str = "
781                SELECT DISTINCT pt1.photo_id
782                   FROM photo_tags pt1
783                INNER JOIN photo_tags pt2
784                   ON pt1.photo_id=pt2.photo_id
785                INNER JOIN tags t
786                   ON pt2.tag_id=t.id
787                INNER JOIN photos p
788                   ON pt1.photo_id=p.id
789                WHERE pt1.tag_id IN (". $selected .")
790             ";
791             if(isset($additional_where_cond)) 
792                $query_str.= "AND ". $additional_where_cond ." ";
793
794             if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
795                $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
796             }
797
798             if(isset($order_str))
799                $query_str.= $order_str;
800          }
801          /* photo has to match all selected tags */
802          elseif($_SESSION['tag_condition'] == 'and') {
803
804             if(count($_SESSION['selected_tags']) >= 32) {
805                print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
806                print "evaluate your tag selection. Please remove some tags from your selection.\n";
807                return Array();
808             } 
809
810             /* Join together a table looking like
811
812                pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
813
814                so the query can quickly return all images matching the
815                selected tags in an AND condition
816
817             */
818
819             $query_str = "
820                SELECT DISTINCT pt1.photo_id
821                   FROM photo_tags pt1
822             ";
823
824             if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
825                $query_str.= "
826                   INNER JOIN tags t
827                      ON pt1.tag_id=t.id
828                ";
829             }
830
831             for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
832                $query_str.= "
833                   INNER JOIN photo_tags pt". ($i+2) ."
834                      ON pt1.photo_id=pt". ($i+2) .".photo_id
835                ";
836             }
837             $query_str.= "
838                INNER JOIN photos p
839                   ON pt1.photo_id=p.id
840             ";
841             $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
842             for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
843                $query_str.= "
844                   AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
845                "; 
846             }
847             if(isset($additional_where_cond)) 
848                $query_str.= "AND ". $additional_where_cond;
849
850             if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
851                $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
852             }
853
854             if(isset($order_str))
855                $query_str.= $order_str;
856
857          }
858
859          $result = $this->db->db_query($query_str);
860          while($row = $this->db->db_fetch_object($result)) {
861             array_push($matched_photos, $row['photo_id']);
862          }
863          return $matched_photos;
864       }
865
866       /* return all available photos */
867       $query_str = "
868          SELECT p.id
869          FROM photos p
870          LEFT JOIN photo_tags pt
871             ON p.id=pt.photo_id
872          LEFT JOIN tags t
873             ON pt.tag_id=t.id
874       ";
875
876       if(isset($additional_where_cond)) 
877          $query_str.= "WHERE ". $additional_where_cond ." ";
878
879       if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
880          $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
881       }
882  
883       if(isset($order_str))
884          $query_str.= $order_str;
885
886       $result = $this->db->db_query($query_str);
887       while($row = $this->db->db_fetch_object($result)) {
888          array_push($matched_photos, $row['id']);
889       }
890       return $matched_photos;
891
892    } // getPhotoSelection()
893
894     /**
895     * control HTML ouput for photo index
896     *
897     * this function provides all the necessary information
898     * for the photo index template.
899     */
900    public function showPhotoIndex()
901    {
902       $photos = $this->getPhotoSelection();
903
904       $count = count($photos);
905
906       if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
907          $anchor = $_SESSION['begin_with'];
908
909       if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
910
911          $begin_with = 0;
912          $end_with = $count;
913
914       }
915       elseif($this->cfg->thumbs_per_page > 0) {
916
917          if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
918             $begin_with = 0;
919          }
920          else {
921             $begin_with = $_SESSION['begin_with'];
922          }
923
924          $end_with = $begin_with + $this->cfg->thumbs_per_page;
925       }
926
927       $thumbs = 0;
928       $images[$thumbs] = Array();
929       $img_height[$thumbs] = Array();
930       $img_width[$thumbs] = Array();
931       $img_id[$thumbs] = Array();
932       $img_name[$thumbs] = Array();
933       $img_title = Array();
934
935       for($i = $begin_with; $i < $end_with; $i++) {
936
937          if(isset($photos[$i])) {
938
939             $images[$thumbs] = $photos[$i];
940             $img_id[$thumbs] = $i;
941             $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
942             $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
943
944             $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
945
946             if(file_exists($thumb_path)) {
947                $info = getimagesize($thumb_path); 
948                $img_width[$thumbs] = $info[0];
949                $img_height[$thumbs] = $info[1];
950             }
951             $thumbs++;
952          } 
953       }
954
955       // +1 for for smarty's selection iteration
956       $thumbs++;
957
958       if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
959          $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
960
961       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
962          $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
963          $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
964       }
965
966       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
967          $this->tmpl->assign('tag_result', 1);
968       }
969
970       /* do we have to display the page selector ? */
971       if($this->cfg->thumbs_per_page != 0) {
972
973          $page_select = "";
974       
975          /* calculate the page switchers */
976          $previous_start = $begin_with - $this->cfg->thumbs_per_page;
977          $next_start = $begin_with + $this->cfg->thumbs_per_page;
978
979          if($begin_with != 0) 
980             $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");"); 
981          if($end_with < $count)
982             $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");"); 
983
984          $photo_per_page  = $this->cfg->thumbs_per_page;
985          $last_page = ceil($count / $photo_per_page);
986
987          /* get the current selected page */
988          if($begin_with == 0) {
989             $current_page = 1;
990          } else {
991             $current_page = 0;
992             for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
993                $current_page++;
994             }
995          } 
996
997          $dotdot_made = 0;
998
999          for($i = 1; $i <= $last_page; $i++) {
1000
1001             if($current_page == $i)
1002                $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1003             elseif($current_page-1 == $i || $current_page+1 == $i)
1004                $style = "style=\"font-size: 105%;\"";
1005             elseif(($current_page-5 >= $i) && ($i != 1) ||
1006                ($current_page+5 <= $i) && ($i != $last_page))
1007                $style = "style=\"font-size: 75%;\"";
1008             else
1009                $style = "";
1010
1011             $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1012                if($style != "")
1013                   $select.= $style;
1014             $select.= ">". $i ."</a>&nbsp;";
1015
1016             // until 9 pages we show the selector from 1-9
1017             if($last_page <= 9) {
1018                $page_select.= $select;
1019                continue;
1020             } else {
1021                if($i == 1 /* first page */ || 
1022                   $i == $last_page /* last page */ ||
1023                   $i == $current_page /* current page */ ||
1024                   $i == ceil($last_page * 0.25) /* first quater */ ||
1025                   $i == ceil($last_page * 0.5) /* half */ ||
1026                   $i == ceil($last_page * 0.75) /* third quater */ ||
1027                   (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1028                   (in_array($i, array($last_page, $last_page-1, $last_page-2, $last_page-3, $last_page-4, $last_page-5)) && $current_page >= $last_page-4) /* the last 6 */ ||
1029                   $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1030                   $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1031
1032                   $page_select.= $select;
1033                   $dotdot_made = 0;
1034                   continue;
1035
1036                }
1037             }
1038
1039             if(!$dotdot_made) {
1040                $page_select.= ".........&nbsp;";
1041                $dotdot_made = 1;
1042             }
1043          }
1044
1045          /* only show the page selector if we have more then one page */
1046          if($last_page > 1)
1047             $this->tmpl->assign('page_selector', $page_select);
1048       }
1049
1050       
1051       $current_tags = $this->getCurrentTags();
1052       $extern_link = "index.php?mode=showpi";
1053       $rss_link = "index.php?mode=rss";
1054       if($current_tags != "") {
1055          $extern_link.= "&tags=". $current_tags;
1056          $rss_link.= "&tags=". $current_tags;
1057       }
1058       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1059          $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1060          $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1061       }
1062
1063       $export_link = "index.php?mode=export";
1064       $slideshow_link = "index.php?mode=slideshow";
1065
1066       $this->tmpl->assign('extern_link', $extern_link);
1067       $this->tmpl->assign('slideshow_link', $slideshow_link);
1068       $this->tmpl->assign('export_link', $export_link);
1069       $this->tmpl->assign('rss_link', $rss_link);
1070       $this->tmpl->assign('count', $count);
1071       $this->tmpl->assign('width', $this->cfg->thumb_width);
1072       $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1073       $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1074       $this->tmpl->assign('images', $images);
1075       $this->tmpl->assign('img_width', $img_width);
1076       $this->tmpl->assign('img_height', $img_height);
1077       $this->tmpl->assign('img_id', $img_id);
1078       $this->tmpl->assign('img_name', $img_name);
1079       $this->tmpl->assign('img_title', $img_title);
1080       $this->tmpl->assign('thumbs', $thumbs);
1081
1082       $this->tmpl->show("photo_index.tpl");
1083
1084       if(isset($anchor))
1085          print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1086
1087    } // showPhotoIndex()
1088
1089    /**
1090     * show credit template
1091     */
1092    public function showCredits()
1093    {
1094       $this->tmpl->assign('version', $this->cfg->version);
1095       $this->tmpl->assign('product', $this->cfg->product);
1096       $this->tmpl->assign('db_version', $this->dbver);
1097       $this->tmpl->show("credits.tpl");
1098
1099    } // showCredits()
1100
1101    /**
1102     * create_thumbnails for the requested width
1103     *
1104     * this function creates image thumbnails of $orig_image
1105     * stored as $thumb_image. It will check if the image is
1106     * in a supported format, if necessary rotate the image
1107     * (based on EXIF orientation meta headers) and re-sizing.
1108     */
1109    public function create_thumbnail($orig_image, $thumb_image, $width)
1110    {  
1111       if(!file_exists($orig_image)) {
1112          return false;
1113       }
1114
1115       $details = getimagesize($orig_image);
1116       
1117       /* check if original photo is a support image type */
1118       if(!$this->checkifImageSupported($details['mime']))
1119          return false;
1120
1121       $meta = $this->get_meta_informations($orig_image);
1122
1123       $rotate = 0;
1124       $flip = false;
1125
1126       switch($meta['Orientation']) {
1127
1128          case 1: /* top, left */
1129             $rotate = 0; $flip = false; break;
1130          case 2: /* top, right */
1131             $rotate = 0; $flip = true; break;
1132          case 3: /* bottom, left */
1133             $rotate = 180; $flip = false; break;
1134          case 4: /* bottom, right */
1135             $rotate = 180; $flip = true; break;
1136          case 5: /* left side, top */
1137             $rotate = 90; $flip = true; break;
1138          case 6: /* right side, top */
1139             $rotate = 90; $flip = false; break;
1140          case 7: /* left side, bottom */
1141             $rotate = 270; $flip = true; break;
1142          case 8: /* right side, bottom */
1143             $rotate = 270; $flip = false; break;
1144       }
1145
1146       $src_img = @imagecreatefromjpeg($orig_image);
1147
1148       if(!$src_img) {
1149          print "Can't load image from ". $orig_image ."\n";
1150          return false;
1151       }
1152
1153       /* grabs the height and width */
1154       $cur_width = imagesx($src_img);
1155       $cur_height = imagesy($src_img);
1156
1157       // If requested width is more then the actual image width,
1158       // do not generate a thumbnail, instead safe the original
1159       // as thumbnail but with lower quality. But if the image
1160       // is to heigh too, then we still have to resize it.
1161       if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1162          $result = imagejpeg($src_img, $thumb_image, 75);
1163          imagedestroy($src_img);
1164          return true;
1165       }
1166
1167       // If the image will be rotate because EXIF orientation said so
1168       // 'virtually rotate' the image for further calculations
1169       if($rotate == 90 || $rotate == 270) {
1170          $tmp = $cur_width;
1171          $cur_width = $cur_height;
1172          $cur_height = $tmp;
1173       }
1174
1175       /* calculates aspect ratio */
1176       $aspect_ratio = $cur_height / $cur_width;
1177
1178       /* sets new size */
1179       if($aspect_ratio < 1) {
1180          $new_w = $width;
1181          $new_h = abs($new_w * $aspect_ratio);
1182       } else {
1183          /* 'virtually' rotate the image and calculate it's ratio */
1184          $tmp_w = $cur_height;
1185          $tmp_h = $cur_width;
1186          /* now get the ratio from the 'rotated' image */
1187          $tmp_ratio = $tmp_h/$tmp_w;
1188          /* now calculate the new dimensions */
1189          $tmp_w = $width;
1190          $tmp_h = abs($tmp_w * $tmp_ratio);
1191
1192          // now that we know, how high they photo should be, if it
1193          // gets rotated, use this high to scale the image
1194          $new_h = $tmp_h;
1195          $new_w = abs($new_h / $aspect_ratio);
1196
1197          // If the image will be rotate because EXIF orientation said so
1198          // now 'virtually rotate' back the image for the image manipulation
1199          if($rotate == 90 || $rotate == 270) {
1200             $tmp = $new_w;
1201             $new_w = $new_h;
1202             $new_h = $tmp;
1203          }
1204       }
1205
1206       /* creates new image of that size */
1207       $dst_img = imagecreatetruecolor($new_w, $new_h);
1208
1209       imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1210
1211       /* copies resized portion of original image into new image */
1212       imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1213
1214       /* needs the image to be flipped horizontal? */
1215       if($flip) {
1216          print "(FLIP)";
1217          $image = $dst_img;
1218          for($x = 0; $x < $new_w; $x++) {
1219             imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1220          }
1221       }
1222
1223       if($rotate) {
1224          $this->_debug("(ROTATE)");
1225          $dst_img = $this->rotateImage($dst_img, $rotate);
1226       }
1227
1228       /* write down new generated file */
1229       $result = imagejpeg($dst_img, $thumb_image, 75);
1230
1231       /* free your mind */
1232       imagedestroy($dst_img);
1233       imagedestroy($src_img);
1234
1235       if($result === false) {
1236          print "Can't write thumbnail ". $thumb_image ."\n";
1237          return false;
1238       }
1239
1240       return true;
1241
1242    } // create_thumbnail()
1243
1244    /**
1245     * return all exif meta data from the file
1246     */
1247    public function get_meta_informations($file)
1248    {
1249       return exif_read_data($file);
1250
1251    } // get_meta_informations()
1252
1253    /**
1254     * create phpfspot own sqlite database
1255     *
1256     * this function creates phpfspots own sqlite database
1257     * if it does not exist yet. this own is used to store
1258     * some necessary informations (md5 sum's, ...).
1259     */
1260    public function check_config_table()
1261    {
1262       // if the config table doesn't exist yet, create it
1263       if(!$this->cfg_db->db_check_table_exists("images")) {
1264          $this->cfg_db->db_exec("
1265             CREATE TABLE images (
1266                img_idx int primary key,
1267                img_md5 varchar(32)
1268             )
1269             ");
1270       }
1271
1272    } // check_config_table
1273
1274    /**
1275     * Generates a thumbnail from photo idx
1276     *
1277     * This function will generate JPEG thumbnails from provided F-Spot photo
1278     * indizes.
1279     *
1280     * 1. Check if all thumbnail generations (width) are already in place and
1281     *    readable
1282     * 2. Check if the md5sum of the original file has changed
1283     * 3. Generate the thumbnails if needed
1284     */
1285    public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1286    {
1287       $error = 0;
1288
1289       $resolutions = Array(
1290          $this->cfg->thumb_width,
1291          $this->cfg->photo_width,
1292          $this->cfg->mini_width,
1293       );
1294
1295       /* get details from F-Spot's database */
1296       $details = $this->get_photo_details($idx);
1297
1298       /* calculate file MD5 sum */
1299       $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1300
1301       if(!file_exists($full_path)) {
1302          $this->_error("File ". $full_path ." does not exist\n");
1303          return;
1304       }
1305
1306       if(!is_readable($full_path)) {
1307          $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1308          return;
1309       }
1310
1311       $file_md5 = md5_file($full_path);
1312
1313       $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1314
1315       $changes = false;
1316
1317       foreach($resolutions as $resolution) {
1318    
1319          $generate_it = false;
1320
1321          $thumb_sub_path = substr($file_md5, 0, 2);
1322          $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1323
1324          if(!file_exists(dirname($thumb_path))) {
1325             mkdir(dirname($thumb_path), 0755);
1326          }
1327
1328          /* if the thumbnail file doesn't exist, create it */
1329          if(!file_exists($thumb_path)) {
1330             $generate_it = true;
1331          }
1332          /* if the file hasn't changed there is no need to regen the thumb */
1333          elseif($file_md5 != $this->getMD5($idx) || $force) {
1334             $generate_it = true;
1335          }
1336
1337          if($generate_it || $overwrite) {
1338
1339             $this->_debug(" ". $resolution ."px");
1340             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1341                $error = 1;
1342
1343             $changes = true;
1344          }
1345       }
1346
1347       if(!$changes) {
1348          $this->_debug(" already exist");
1349       }
1350
1351       /* set the new/changed MD5 sum for the current photo */
1352       if(!$error) {
1353          $this->setMD5($idx, $file_md5);
1354       }
1355
1356       $this->_debug("\n");
1357
1358    } // gen_thumb()
1359
1360    /**
1361     * returns stored md5 sum for a specific photo
1362     *
1363     * this function queries the phpfspot database for a
1364     * stored MD5 checksum of the specified photo
1365     */
1366    public function getMD5($idx)
1367    {
1368       $result = $this->cfg_db->db_query("
1369          SELECT img_md5 
1370          FROM images
1371          WHERE img_idx='". $idx ."'
1372       ");
1373
1374       if(!$result)
1375          return 0;
1376
1377       $img = $this->cfg_db->db_fetch_object($result);
1378       return $img['img_md5'];
1379       
1380    } // getMD5()
1381
1382    /**
1383     * set MD5 sum for the specific photo
1384     */
1385    private function setMD5($idx, $md5)
1386    {
1387       $result = $this->cfg_db->db_exec("
1388          REPLACE INTO images (img_idx, img_md5)
1389          VALUES ('". $idx ."', '". $md5 ."')
1390       ");
1391
1392    } // setMD5()
1393
1394    /**
1395     * store current tag condition
1396     *
1397     * this function stores the current tag condition
1398     * (AND or OR) in the users session variables
1399     */
1400    public function setTagCondition($mode)
1401    {
1402       $_SESSION['tag_condition'] = $mode;
1403
1404    } // setTagCondition()
1405
1406    /** 
1407     * invoke tag & date search 
1408     *
1409     * this function will return all matching tags and store
1410     * them in the session variable selected_tags. furthermore
1411     * it also handles the date search.
1412     * getPhotoSelection() will then only return the matching
1413     * photos.
1414     */
1415    public function startSearch($searchfor, $from = 0, $to = 0)
1416    {
1417       $this->get_tags();
1418
1419       $_SESSION['searchfor'] = $searchfor;
1420
1421       if($from != 0)
1422          $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1423       else
1424          unset($_SESSION['from_date']);
1425
1426       if($to != 0)
1427          $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1428       else
1429          unset($_SESSION['to_date']);
1430
1431       if($searchfor != "") {
1432          /* new search, reset the current selected tags */
1433          $_SESSION['selected_tags'] = Array();
1434          foreach($this->avail_tags as $tag) {
1435             if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1436                array_push($_SESSION['selected_tags'], $tag);
1437          }
1438       }
1439
1440    } // startSearch()
1441
1442    /**
1443     * updates sort order in session variable
1444     *
1445     * this function is invoked by RPC and will sort the requested
1446     * sort order in the session variable.
1447     */
1448    public function updateSortOrder($order)
1449    {
1450       if(isset($this->sort_orders[$order])) {
1451          $_SESSION['sort_order'] = $order;
1452          return "ok";
1453       }
1454
1455       return "unkown error";
1456
1457    } // updateSortOrder()
1458
1459    /**
1460     * rotate image
1461     *
1462     * this function rotates the image according the
1463     * specified angel.
1464     */
1465    private function rotateImage($img, $degrees)
1466    {
1467       if(function_exists("imagerotate")) {
1468          $img = imagerotate($img, $degrees, 0);
1469       } else {
1470          function imagerotate($src_img, $angle)
1471          {
1472             $src_x = imagesx($src_img);
1473             $src_y = imagesy($src_img);
1474             if ($angle == 180) {
1475                $dest_x = $src_x;
1476                $dest_y = $src_y;
1477             }
1478             elseif ($src_x <= $src_y) {
1479                $dest_x = $src_y;
1480                $dest_y = $src_x;
1481             }
1482             elseif ($src_x >= $src_y) {
1483                $dest_x = $src_y;
1484                $dest_y = $src_x;
1485             }
1486                
1487             $rotate=imagecreatetruecolor($dest_x,$dest_y);
1488             imagealphablending($rotate, false);
1489                
1490             switch ($angle) {
1491             
1492                case 90:
1493                   for ($y = 0; $y < ($src_y); $y++) {
1494                      for ($x = 0; $x < ($src_x); $x++) {
1495                         $color = imagecolorat($src_img, $x, $y);
1496                         imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1497                      }
1498                   }
1499                   break;
1500
1501                case 270:
1502                   for ($y = 0; $y < ($src_y); $y++) {
1503                      for ($x = 0; $x < ($src_x); $x++) {
1504                         $color = imagecolorat($src_img, $x, $y);
1505                         imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1506                      }
1507                   }
1508                   break;
1509
1510                case 180:
1511                   for ($y = 0; $y < ($src_y); $y++) {
1512                      for ($x = 0; $x < ($src_x); $x++) {
1513                         $color = imagecolorat($src_img, $x, $y);
1514                         imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1515                      }
1516                   }
1517                   break;
1518
1519                default:
1520                   $rotate = $src_img;
1521                   break;
1522             };
1523
1524             return $rotate;
1525
1526          }
1527
1528          $img = imagerotate($img, $degrees);
1529
1530       }
1531
1532       return $img;
1533
1534    } // rotateImage()
1535
1536    /**
1537     * return all assigned tags for the specified photo
1538     */
1539    private function get_photo_tags($idx)
1540    {
1541       $result = $this->db->db_query("
1542          SELECT t.id, t.name
1543          FROM tags t
1544          INNER JOIN photo_tags pt
1545             ON t.id=pt.tag_id
1546          WHERE pt.photo_id='". $idx ."'
1547       ");
1548
1549       $tags = Array();
1550
1551       while($row = $this->db->db_fetch_object($result))
1552          $tags[$row['id']] = $row['name'];
1553
1554       return $tags;
1555
1556    } // get_photo_tags()
1557
1558    /**
1559     * create on-the-fly images with text within
1560     */
1561    public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1562    {
1563       if (strlen($color) != 6) 
1564          $color = 000000;
1565
1566       $int = hexdec($color);
1567       $h = imagefontheight($font);
1568       $fw = imagefontwidth($font);
1569       $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1570       $lines = count($txt);
1571       $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1572       $bg = imagecolorallocate($im, 255, 255, 255);
1573       $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1574       $y = 0;
1575
1576       foreach ($txt as $text) {
1577          $x = (($w - ($fw * strlen($text))) / 2);
1578          imagestring($im, $font, $x, $y, $text, $color);
1579          $y += ($h + $space);
1580       }
1581
1582       Header("Content-type: image/png");
1583       ImagePng($im);
1584
1585    } // showTextImage()
1586
1587    /**
1588     * check if all requirements are met
1589     */
1590    private function checkRequirements()
1591    {
1592       if(!function_exists("imagecreatefromjpeg")) {
1593          print "PHP GD library extension is missing<br />\n";
1594          $missing = true;
1595       }
1596
1597       if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1598          print "PHP SQLite3 library extension is missing<br />\n";
1599          $missing = true;
1600       }
1601
1602       /* Check for HTML_AJAX PEAR package, lent from Horde project */
1603       ini_set('track_errors', 1);
1604       @include_once 'HTML/AJAX/Server.php';
1605       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1606          print "PEAR HTML_AJAX package is missing<br />\n";
1607          $missing = true;
1608       }
1609       @include_once 'Calendar/Calendar.php';
1610       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1611          print "PEAR Calendar package is missing<br />\n";
1612          $missing = true;
1613       }
1614       @include_once 'Console/Getopt.php';
1615       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1616          print "PEAR Console_Getopt package is missing<br />\n";
1617          $missing = true;
1618       }
1619       ini_restore('track_errors');
1620
1621       if(isset($missing))
1622          return false;
1623
1624       return true;
1625
1626    } // checkRequirements()
1627
1628    private function _debug($text)
1629    {
1630       if($this->fromcmd) {
1631          print $text;
1632       }
1633
1634    } // _debug()
1635
1636    /**
1637     * check if specified MIME type is supported
1638     */
1639    public function checkifImageSupported($mime)
1640    {
1641       if(in_array($mime, Array("image/jpeg")))
1642          return true;
1643
1644       return false;
1645
1646    } // checkifImageSupported()
1647
1648    public function _error($text)
1649    {
1650       switch($this->cfg->logging) {
1651          case 'display':
1652             print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1653             print $text;
1654             break;
1655          case 'errorlog':  
1656             error_log($text);
1657             break;
1658          case 'logfile':
1659             error_log($text, 3, $his->cfg->log_file);
1660             break;
1661       }
1662
1663    } // _error()
1664
1665    /**
1666     * output calendard input fields
1667     */
1668    private function get_calendar($mode)
1669    {
1670       $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1671       $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1672       $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1673
1674       $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1675       if(!isset($_SESSION[$mode .'_date']))
1676          $output.= " disabled=\"disabled\"";
1677       $output.= " />\n";
1678       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1679       if(!isset($_SESSION[$mode .'_date']))
1680          $output.= " disabled=\"disabled\"";
1681       $output.= " />\n";
1682       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1683       if(!isset($_SESSION[$mode .'_date']))
1684          $output.= " disabled=\"disabled\"";
1685       $output.= " />\n";
1686
1687       return $output;
1688
1689    } // get_calendar()
1690
1691    /**
1692     * output calendar matrix
1693     */
1694    public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1695    {
1696       if (!isset($year)) $year = date('Y');
1697       if (!isset($month)) $month = date('m');
1698       if (!isset($day)) $day = date('d');
1699       $rows = 1;
1700       $cols = 1;
1701       $matrix = Array();
1702
1703       require_once CALENDAR_ROOT.'Month/Weekdays.php';
1704       require_once CALENDAR_ROOT.'Day.php';
1705
1706       // Build the month
1707       $month = new Calendar_Month_Weekdays($year,$month);
1708
1709       // Create links
1710       $prevStamp = $month->prevMonth(true);
1711       $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1712       $nextStamp = $month->nextMonth(true);
1713       $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1714
1715       $selectedDays = array (
1716          new Calendar_Day($year,$month,$day),
1717          new Calendar_Day($year,12,25),
1718       );
1719
1720       // Build the days in the month
1721       $month->build($selectedDays);
1722
1723       $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1724       $this->tmpl->assign('prev_month', $prev);
1725       $this->tmpl->assign('next_month', $next);
1726
1727       while ( $day = $month->fetch() ) {
1728    
1729          if(!isset($matrix[$rows]))
1730             $matrix[$rows] = Array();
1731
1732          $string = "";
1733
1734          $dayStamp = $day->thisDay(true);
1735          $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1736
1737          // isFirst() to find start of week
1738          if ( $day->isFirst() )
1739             $string.= "<tr>\n";
1740
1741          if ( $day->isSelected() ) {
1742             $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1743          } else if ( $day->isEmpty() ) {
1744             $string.= "<td>&nbsp;</td>\n";
1745          } else {
1746             $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1747          }
1748
1749          // isLast() to find end of week
1750          if ( $day->isLast() )
1751             $string.= "</tr>\n";
1752
1753          $matrix[$rows][$cols] = $string;
1754
1755          $cols++;
1756
1757          if($cols > 7) {
1758             $cols = 1;
1759             $rows++;
1760          }
1761       }
1762
1763       $this->tmpl->assign('matrix', $matrix);
1764       $this->tmpl->assign('rows', $rows);
1765       $this->tmpl->show("calendar.tpl");
1766
1767    } // get_calendar_matrix()
1768
1769    /**
1770     * output export page
1771     */
1772    public function getExport($mode)
1773    {
1774       $pictures = $this->getPhotoSelection();
1775       $current_tags = $this->getCurrentTags();  
1776
1777       foreach($pictures as $picture) {
1778
1779          $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1780          if($current_tags != "") {
1781             $orig_url.= "&tags=". $current_tags;
1782          } 
1783          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1784             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1785          }
1786
1787          $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1788
1789          switch($mode) {
1790
1791             case 'HTML':
1792                // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1793                print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1794                break;
1795                
1796             case 'MoinMoin':
1797                // "[%pictureurl% %thumbnailurl%]"
1798                print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1799                break;
1800
1801             case 'MoinMoinList':
1802                // " * [%pictureurl% %thumbnailurl%]"
1803                print "&nbsp;" . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1804                break;
1805          }
1806
1807       }
1808
1809    } // getExport()
1810
1811    /**
1812     * output RSS feed
1813     */
1814    public function getRSSFeed()
1815    {
1816       Header("Content-type: text/xml; charset=utf-8");
1817       print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1818 ?>
1819 <rss version="2.0"
1820    xmlns:media="http://search.yahoo.com/mrss/"
1821    xmlns:dc="http://purl.org/dc/elements/1.1/"
1822  >
1823  <channel>
1824   <title>phpfspot</title>
1825   <description>phpfspot RSS feed</description>
1826   <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1827   <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1828   <generator>phpfspot</generator>
1829 <?php
1830
1831       $pictures = $this->getPhotoSelection();
1832       $current_tags = $this->getCurrentTags();  
1833
1834       foreach($pictures as $picture) {
1835
1836          $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1837          if($current_tags != "") {
1838             $orig_url.= "&tags=". $current_tags;
1839          } 
1840          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1841             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1842          }
1843
1844          $details = $this->get_photo_details($picture);
1845
1846          $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1847          $thumb_html = htmlspecialchars("
1848 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1849 <br>
1850 ". $details['description']);
1851
1852          $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1853          $meta = $this->get_meta_informations($orig_path);
1854          $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1855
1856 ?>
1857   <item>
1858    <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1859    <link><?php print htmlspecialchars($orig_url); ?></link>
1860    <guid><?php print htmlspecialchars($orig_url); ?></guid>
1861    <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1862    <description>
1863     <?php print $thumb_html; ?> 
1864    </description>
1865    <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1866   </item>
1867 <?php
1868
1869       }
1870 ?>
1871  </channel>
1872 </rss>
1873 <?php
1874
1875
1876    } // getExport()
1877
1878  
1879    /**
1880     * return all selected tags as one string
1881     */
1882    private function getCurrentTags()
1883    {
1884       $current_tags = "";
1885       if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1886          foreach($_SESSION['selected_tags'] as $tag)
1887             $current_tags.= $tag .",";
1888          $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1889       }
1890       return $current_tags;
1891
1892    } // getCurrentTags()
1893
1894    /**
1895     * return the current photo
1896     */
1897    public function getCurrentPhoto()
1898    {
1899       if(isset($_SESSION['current_photo'])) {
1900          print $_SESSION['current_photo'];
1901       }
1902    } // getCurrentPhoto()
1903
1904    /**
1905     * tells the client browser what to do
1906     *
1907     * this function is getting called via AJAX by the
1908     * client browsers. it will tell them what they have
1909     * to do next. This is necessary for directly jumping
1910     * into photo index or single photo view when the are
1911     * requested with specific URLs
1912     */
1913    public function whatToDo()
1914    {
1915       if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1916          return "show_photo";
1917       }
1918       elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1919          return "showpi_tags";
1920       }
1921       elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1922          return "showpi";
1923       }
1924
1925       return "nothing special";
1926
1927    } // whatToDo()
1928
1929    /**
1930     * return the current process-user
1931     */
1932    private function getuid()
1933    {
1934       if($uid = posix_getuid()) {
1935          if($user = posix_getpwuid($uid)) {
1936             return $user['name'];
1937          }
1938       }
1939    
1940       return 'n/a';
1941    
1942    } // getuid()
1943
1944    /**
1945     * returns a select-dropdown box to select photo index sort parameters
1946     */
1947    public function smarty_sort_select_list($params, &$smarty)
1948    {
1949       $output = "";
1950
1951       foreach($this->sort_orders as $key => $value) {
1952          $output.= "<option value=\"". $key ."\"";
1953          if($key == $_SESSION['sort_order']) {
1954             $output.= " selected=\"selected\"";
1955          }
1956          $output.= ">". $value ."</option>";
1957       }
1958
1959       return $output;
1960
1961    } // smarty_sort_select_list()
1962
1963    /**
1964     * returns the currently selected sort order
1965     */ 
1966    private function get_sort_order()
1967    {
1968       switch($_SESSION['sort_order']) {
1969          case 'date_asc':
1970             return " ORDER BY p.time ASC";
1971             break;
1972          case 'date_desc':
1973             return " ORDER BY p.time DESC";
1974             break;
1975          case 'name_asc':
1976             if($this->dbver < 9) {
1977                return " ORDER BY p.name ASC";
1978             }
1979             else {
1980                return " ORDER BY basename(p.uri) ASC";
1981             }
1982             break;
1983          case 'name_desc':
1984             if($this->dbver < 9) {
1985                return " ORDER BY p.name DESC";
1986             }
1987             else {
1988                return " ORDER BY basename(p.uri) DESC";
1989             }
1990             break;
1991       }
1992
1993    } // get_sort_order()
1994
1995    /***
1996      * return the next to be shown slide show image
1997      *
1998      * this function returns the URL of the next image
1999      * in the slideshow sequence.
2000      */
2001    public function getNextSlideShowImage()
2002    {
2003       $all_photos = $this->getPhotoSelection();
2004
2005       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1) 
2006          $_SESSION['slideshow_img'] = 0;
2007       else
2008          $_SESSION['slideshow_img']++;
2009
2010       return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2011
2012    } // getNextSlideShowImage()
2013
2014    /***
2015      * return the previous to be shown slide show image
2016      *
2017      * this function returns the URL of the previous image
2018      * in the slideshow sequence.
2019      */
2020    public function getPrevSlideShowImage()
2021    {
2022       $all_photos = $this->getPhotoSelection();
2023
2024       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2025          $_SESSION['slideshow_img'] = 0;
2026       else
2027          $_SESSION['slideshow_img']--;
2028
2029       return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2030
2031    } // getPrevSlideShowImage()
2032
2033    public function resetSlideShow()
2034    {
2035       if(isset($_SESSION['slideshow_img']))
2036          unset($_SESSION['slideshow_img']);
2037    } // resetSlideShow()
2038    
2039    /***
2040      * get random photo
2041      *
2042      * this function will get all photos from the fspot
2043      * database and randomly return ONE entry
2044      *
2045      * saddly there is yet no sqlite3 function which returns
2046      * the bulk result in array, so we have to fill up our
2047      * own here.
2048      */ 
2049    public function get_random_photo()
2050    {
2051       $all = Array();
2052
2053       $result = $this->db->db_query("
2054          SELECT id
2055          FROM photos
2056       ");
2057       
2058       while($row = $this->db->db_fetch_object($result)) {
2059          array_push($all, $row['id']);
2060       }
2061
2062       return $all[array_rand($all)];
2063
2064    } // get_random_photo()
2065
2066    /**
2067     * validates provided date
2068     *
2069     * this function validates if the provided date
2070     * contains a valid date and will return true 
2071     * if it is.
2072     */
2073    public function isValidDate($date_str)
2074    {
2075       $timestamp = strtotime($date_str);
2076    
2077       if(is_numeric($timestamp))
2078          return true;
2079       
2080       return false;
2081
2082    } // isValidDate()
2083
2084    /**
2085     * timestamp to string conversion
2086     */
2087    private function ts2str($timestamp)
2088    {
2089       return strftime("%Y-%m-%d", $timestamp);
2090    } // ts2str()
2091
2092    private function extractTags($tags_str)
2093    {
2094       $not_validated = split(',', $_GET['tags']);
2095       $validated = array();
2096
2097       foreach($not_validated as $tag) {
2098          if(is_numeric($tag))
2099             array_push($validated, $tag);
2100       }
2101    
2102       return $validated;
2103    
2104    } // extractTags()
2105
2106    /**
2107     * returns the full path to a thumbnail
2108     */
2109    public function get_thumb_path($width, $photo)
2110    {
2111       $md5 = $this->getMD5($photo);
2112       $sub_path = substr($md5, 0, 2);
2113       return $this->cfg->thumb_path
2114          . "/"
2115          . $sub_path
2116          . "/"
2117          . $width
2118          . "_"
2119          . $md5;
2120
2121    } // get_thumb_path()
2122
2123    /**
2124     * returns server's virtual host name
2125     */
2126    private function get_server_name()
2127    {
2128       return $_SERVER['SERVER_NAME'];
2129    } // get_server_name()
2130
2131    /**
2132     * returns type of webprotocol which is
2133     * currently used
2134     */
2135    private function get_web_protocol()
2136    {
2137       if(!isset($_SERVER['HTTPS']))
2138          return "http";
2139       else
2140          return "https";
2141    } // get_web_protocol()
2142
2143    /**
2144     * return url to this phpfspot installation
2145     */
2146    private function get_phpfspot_url()
2147    {
2148       return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2149    } // get_phpfspot_url()
2150    
2151    /**
2152     * check file exists and is readable
2153     *
2154     * returns true, if everything is ok, otherwise false
2155     * if $silent is not set, this function will output and
2156     * error message
2157     */
2158    private function check_readable($file, $silent = null)
2159    {
2160       if(!file_exists($file)) {
2161          if(!isset($silent))
2162             print "File \"". $file ."\" does not exist.\n";
2163          return false;
2164       }
2165
2166       if(!is_readable($file)) {
2167          if(!isset($silent))
2168             print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2169          return false;
2170       }
2171
2172       return true;
2173
2174    } // check_readable()
2175
2176    /**
2177     * check if all needed indices are present
2178     *
2179     * this function checks, if some needed indices are already
2180     * present, or if not, create them on the fly. they are
2181     * necessary to speed up some queries like that one look for
2182     * all tags, when show_tags is specified in the configuration.
2183     */
2184    private function checkDbIndices()
2185    {
2186       $result = $this->db->db_exec("
2187          CREATE INDEX IF NOT EXISTS
2188             phototag
2189          ON
2190             photo_tags
2191                (photo_id, tag_id)
2192       ");
2193
2194    } // checkDbIndices()
2195
2196    /**
2197     * retrive F-Spot database version
2198     *
2199     * this function will return the F-Spot database version number
2200     * It is stored within the sqlite3 database in the table meta
2201     */
2202    public function getFspotDBVersion()
2203    {
2204       if($result = $this->db->db_fetchSingleRow("
2205          SELECT data as version
2206          FROM meta
2207          WHERE
2208             name LIKE 'F-Spot Database Version'
2209       "))
2210          return $result['version'];
2211
2212       return null;
2213
2214    } // getFspotDBVersion()
2215
2216    /**
2217     * parse the provided URI and will returned the
2218     * requested chunk
2219     */
2220    public function parse_uri($uri, $mode)
2221    {
2222       if(($components = parse_url($uri)) !== false) {
2223
2224          switch($mode) {
2225             case 'filename':
2226                return basename($components['path']);
2227                break;
2228             case 'dirname':
2229                return dirname($components['path']);
2230                break;
2231             case 'fullpath':
2232                return $components['path'];
2233                break;
2234          }
2235       }
2236
2237       return $uri;
2238
2239    } // parse_uri()
2240
2241 } // class PHPFSPOT
2242
2243 ?>