sort-order is now set via AJAX, not while submit a search-query
[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 rows_per_page is now 0, unset it
116       if(isset($_SESSION['begin_with']) && $this->cfg->rows_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']);
169                }
170                if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
171                   $_SESSION['to_date'] = strtotime($_GET['to_date']);
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
477          $info = getimagesize($thumb_path);
478
479          $this->tmpl->assign('description', $details['description']);
480          $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
481
482          $this->tmpl->assign('width', $info[0]);
483          $this->tmpl->assign('height', $info[1]);
484          $this->tmpl->assign('ExifMadeOn', $meta_date);
485          $this->tmpl->assign('ExifMadeWith', $meta_make);
486          $this->tmpl->assign('ExifOrigResolution', $meta_res);
487          $this->tmpl->assign('ExifFileSize', $meta_size);
488     
489          $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&amp;width=". $this->cfg->photo_width);
490          $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
491
492          $this->tmpl->assign('tags', $this->get_photo_tags($photo));
493          $this->tmpl->assign('current', $current);
494       }
495       else {
496          $this->_error("Can't open file ". $thumb_path ."\n");
497          return;
498       }
499
500       if($previous_img) {
501          $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
502          $this->tmpl->assign('prev_img', $previous_img);
503       }
504
505       if($next_img) {
506          $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
507          $this->tmpl->assign('next_img', $next_img);
508       }
509       $this->tmpl->assign('mini_width', $this->cfg->mini_width);
510       $this->tmpl->assign('photo_number', $i);
511       $this->tmpl->assign('photo_count', count($all_photos));
512
513       $this->tmpl->show("single_photo.tpl");
514
515    } // showPhoto()
516
517    /**
518     * all available tags and tag cloud
519     *
520     * this function outputs all available tags (time ordered)
521     * and in addition output them as tag cloud (tags which have
522     * many photos will appears more then others)
523     */
524    public function getAvailableTags()
525    {
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       $this->get_tags();
598
599       $output = "";
600       foreach($this->avail_tags as $tag)
601       {
602          // return all selected tags
603          if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
604             $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
605          }
606       }
607
608       $output = substr($output, 0, strlen($output)-2);
609       print $output;
610
611    } // getSelectedTags()
612
613    /**
614     * add tag to users session variable
615     *
616     * this function will add the specified to users current
617     * tag selection. if a date search has been made before
618     * it will be now cleared
619     */
620    public function addTag($tag)
621    {
622       if(!isset($_SESSION['selected_tags']))
623          $_SESSION['selected_tags'] = Array();
624
625       if(isset($_SESSION['searchfor']))
626          unset($_SESSION['searchfor']);
627
628       if(!in_array($tag, $_SESSION['selected_tags']))
629          array_push($_SESSION['selected_tags'], $tag);
630    
631    } // addTag()
632
633    /**
634     * remove tag to users session variable
635     *
636     * this function removes the specified tag from
637     * users current tag selection
638     */
639    public function delTag($tag)
640    {
641       if(isset($_SESSION['searchfor']))
642          unset($_SESSION['searchfor']);
643
644       if(isset($_SESSION['selected_tags'])) {
645          $key = array_search($tag, $_SESSION['selected_tags']);
646          unset($_SESSION['selected_tags'][$key]);
647          sort($_SESSION['selected_tags']);
648       }
649
650    } // delTag()
651
652    /**
653     * reset tag selection
654     *
655     * if there is any tag selection, it will be
656     * deleted now
657     */
658    public function resetTags()
659    {
660       if(isset($_SESSION['selected_tags']))
661          unset($_SESSION['selected_tags']);
662
663    } // resetTags()
664
665    /**
666     * reset single photo
667     *
668     * if a specific photo was requested (external link)
669     * unset the session variable now
670     */
671    public function resetPhotoView()
672    {
673       if(isset($_SESSION['current_photo']))
674          unset($_SESSION['current_photo']);
675
676    } // resetPhotoView();
677
678    /**
679     * reset tag search
680     *
681     * if any tag search has taken place, reset
682     * it now
683     */
684    public function resetTagSearch()
685    {
686       if(isset($_SESSION['searchfor']))
687          unset($_SESSION['searchfor']);
688
689    } // resetTagSearch()
690
691     /**
692     * reset date search
693     *
694     * if any date search has taken place, reset
695     * it now
696     */
697    public function resetDateSearch()
698    {
699       if(isset($_SESSION['from_date']))
700          unset($_SESSION['from_date']);
701       if(isset($_SESSION['to_date']))
702          unset($_SESSION['to_date']);
703
704    } // resetDateSearch();
705
706    /**
707     * return all photo according selection
708     *
709     * this function returns all photos based on
710     * the tag-selection, tag- or date-search.
711     * the tag-search also has to take care of AND
712     * and OR conjunctions
713     */
714    public function getPhotoSelection()
715    {  
716       $matched_photos = Array();
717
718       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
719          $from_date = $_SESSION['from_date'];
720          $to_date = $_SESSION['to_date'];
721          $additional_where_cond = "
722                p.time>='". $from_date ."'
723             AND
724                p.time<='". $to_date ."'
725          ";
726       } 
727
728       if(isset($_SESSION['sort_order'])) {
729          $order_str = $this->get_sort_order();
730       }
731
732       /* return a search result */
733       if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
734          $query_str = "
735             SELECT DISTINCT pt1.photo_id
736                FROM photo_tags pt1
737             INNER JOIN photo_tags pt2
738                ON pt1.photo_id=pt2.photo_id
739             INNER JOIN tags t1
740                ON pt1.tag_id=t1.id
741             INNER JOIN photos p
742                ON pt1.photo_id=p.id
743             INNER JOIN tags t2
744                ON pt2.tag_id=t2.id
745             WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
746
747          if(isset($additional_where_cond))
748             $query_str.= "AND ". $additional_where_cond ." ";
749
750          if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
751             $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
752          }
753          
754          if(isset($order_str))
755             $query_str.= $order_str;
756
757          $result = $this->db->db_query($query_str);
758          while($row = $this->db->db_fetch_object($result)) {
759             array_push($matched_photos, $row['photo_id']);
760          }
761          return $matched_photos;
762       }
763
764       /* return according the selected tags */
765       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
766          $selected = "";
767          foreach($_SESSION['selected_tags'] as $tag)
768             $selected.= $tag .",";
769          $selected = substr($selected, 0, strlen($selected)-1);
770
771          /* photo has to match at least on of the selected tags */
772          if($_SESSION['tag_condition'] == 'or') {
773             $query_str = "
774                SELECT DISTINCT pt1.photo_id
775                   FROM photo_tags pt1
776                INNER JOIN photo_tags pt2
777                   ON pt1.photo_id=pt2.photo_id
778                INNER JOIN tags t
779                   ON pt2.tag_id=t.id
780                INNER JOIN photos p
781                   ON pt1.photo_id=p.id
782                WHERE pt1.tag_id IN (". $selected .")
783             ";
784             if(isset($additional_where_cond)) 
785                $query_str.= "AND ". $additional_where_cond ." ";
786
787             if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
788                $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
789             }
790
791             if(isset($order_str))
792                $query_str.= $order_str;
793          }
794          /* photo has to match all selected tags */
795          elseif($_SESSION['tag_condition'] == 'and') {
796
797             if(count($_SESSION['selected_tags']) >= 32) {
798                print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
799                print "evaluate your tag selection. Please remove some tags from your selection.\n";
800                return Array();
801             } 
802
803             /* Join together a table looking like
804
805                pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
806
807                so the query can quickly return all images matching the
808                selected tags in an AND condition
809
810             */
811
812             $query_str = "
813                SELECT DISTINCT pt1.photo_id
814                   FROM photo_tags pt1
815             ";
816
817             if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
818                $query_str.= "
819                   INNER JOIN tags t
820                      ON pt1.tag_id=t.id
821                ";
822             }
823
824             for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
825                $query_str.= "
826                   INNER JOIN photo_tags pt". ($i+2) ."
827                      ON pt1.photo_id=pt". ($i+2) .".photo_id
828                ";
829             }
830             $query_str.= "
831                INNER JOIN photos p
832                   ON pt1.photo_id=p.id
833             ";
834             $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
835             for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
836                $query_str.= "
837                   AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
838                "; 
839             }
840             if(isset($additional_where_cond)) 
841                $query_str.= "AND ". $additional_where_cond;
842
843             if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
844                $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
845             }
846
847             if(isset($order_str))
848                $query_str.= $order_str;
849
850          }
851
852          $result = $this->db->db_query($query_str);
853          while($row = $this->db->db_fetch_object($result)) {
854             array_push($matched_photos, $row['photo_id']);
855          }
856          return $matched_photos;
857       }
858
859       /* return all available photos */
860       $query_str = "
861          SELECT DISTINCT photo_id
862             FROM photo_tags pt
863          INNER JOIN photos p
864             ON p.id=pt.photo_id
865          INNER JOIN tags t
866             ON pt.tag_id=t.id
867       ";
868       if(isset($additional_where_cond)) 
869          $query_str.= "WHERE ". $additional_where_cond ." ";
870
871       if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
872          $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
873       }
874  
875       if(isset($order_str))
876          $query_str.= $order_str;
877
878       $result = $this->db->db_query($query_str);
879       while($row = $this->db->db_fetch_object($result)) {
880          array_push($matched_photos, $row['photo_id']);
881       }
882       return $matched_photos;
883
884    } // getPhotoSelection()
885
886     /**
887     * control HTML ouput for photo index
888     *
889     * this function provides all the necessary information
890     * for the photo index template.
891     */
892    public function showPhotoIndex()
893    {
894       $photos = $this->getPhotoSelection();
895
896       $count = count($photos);
897
898       if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
899          $anchor = $_SESSION['begin_with'];
900
901       if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
902
903          $begin_with = 0;
904          $end_with = $count;
905
906       }
907       elseif($this->cfg->rows_per_page > 0) {
908
909          if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
910             $begin_with = 0;
911          }
912          else {
913
914             $begin_with = $_SESSION['begin_with'];
915
916             // verify $begin_with - perhaps the thumbs-per-rows or
917             // rows-per-page variables have changed or the jump back
918             // from a photo wasn't exact - so calculate the real new
919             // starting point
920             $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
921             for($i = 0; $i <= $count; $i+=$multiplicator) {
922                if($begin_with >= $i && $begin_with < $i+$multiplicator) {
923                   $begin_with = $i;
924                   break;
925                }
926             }
927          }
928
929          $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
930       }
931
932    
933       $rows = 0;
934       $cols = 0;
935       $images[$rows] = Array();
936       $img_height[$rows] = Array();
937       $img_width[$rows] = Array();
938       $img_id[$rows] = Array();
939       $img_name[$rows] = Array();
940       $img_title = Array();
941
942       for($i = $begin_with; $i < $end_with; $i++) {
943
944          if(isset($photos[$i])) {
945
946             $images[$rows][$cols] = $photos[$i];
947             $img_id[$rows][$cols] = $i;
948             $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
949             $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
950
951             $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
952
953             if(file_exists($thumb_path)) {
954                $info = getimagesize($thumb_path); 
955                $img_width[$rows][$cols] = $info[0];
956                $img_height[$rows][$cols] = $info[1];
957             }
958
959             if($cols == $this->cfg->thumbs_per_row-1) {
960                $cols = 0;
961                $rows++;
962                $images[$rows] = Array();
963                $img_width[$rows] = Array();
964                $img_height[$rows] = Array();
965             }
966             else {
967                $cols++;
968             }
969          } 
970       }
971
972       // +1 for for smarty's selection iteration
973       $rows++;
974
975       if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
976          $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
977
978       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
979          $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
980          $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
981       }
982
983       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
984          $this->tmpl->assign('tag_result', 1);
985       }
986
987       /* do we have to display the page selector ? */
988       if($this->cfg->rows_per_page != 0) {
989
990          $page_select = "";
991       
992          /* calculate the page switchers */
993          $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
994          $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
995
996          if($begin_with != 0) 
997             $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");"); 
998          if($end_with < $count)
999             $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");"); 
1000
1001          $photo_per_page  = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
1002          $last_page = ceil($count / $photo_per_page);
1003
1004          /* get the current selected page */
1005          if($begin_with == 0) {
1006             $current_page = 1;
1007          } else {
1008             $current_page = 0;
1009             for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1010                $current_page++;
1011             }
1012          } 
1013
1014          $dotdot_made = 0;
1015
1016          for($i = 1; $i <= $last_page; $i++) {
1017
1018             if($current_page == $i)
1019                $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1020             elseif($current_page-1 == $i || $current_page+1 == $i)
1021                $style = "style=\"font-size: 105%;\"";
1022             elseif(($current_page-5 >= $i) && ($i != 1) ||
1023                ($current_page+5 <= $i) && ($i != $last_page))
1024                $style = "style=\"font-size: 75%;\"";
1025             else
1026                $style = "";
1027
1028             $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1029                if($style != "")
1030                   $select.= $style;
1031             $select.= ">". $i ."</a>&nbsp;";
1032
1033             // until 9 pages we show the selector from 1-9
1034             if($last_page <= 9) {
1035                $page_select.= $select;
1036                continue;
1037             } else {
1038                if($i == 1 /* first page */ || 
1039                   $i == $last_page /* last page */ ||
1040                   $i == $current_page /* current page */ ||
1041                   $i == ceil($last_page * 0.25) /* first quater */ ||
1042                   $i == ceil($last_page * 0.5) /* half */ ||
1043                   $i == ceil($last_page * 0.75) /* third quater */ ||
1044                   (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1045                   (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 */ ||
1046                   $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1047                   $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1048
1049                   $page_select.= $select;
1050                   $dotdot_made = 0;
1051                   continue;
1052
1053                }
1054             }
1055
1056             if(!$dotdot_made) {
1057                $page_select.= ".........&nbsp;";
1058                $dotdot_made = 1;
1059             }
1060          }
1061
1062          /* only show the page selector if we have more then one page */
1063          if($last_page > 1)
1064             $this->tmpl->assign('page_selector', $page_select);
1065       }
1066
1067       
1068       $current_tags = $this->getCurrentTags();
1069       $extern_link = "index.php?mode=showpi";
1070       $rss_link = "index.php?mode=rss";
1071       if($current_tags != "") {
1072          $extern_link.= "&tags=". $current_tags;
1073          $rss_link.= "&tags=". $current_tags;
1074       }
1075       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1076          $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1077          $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1078       }
1079
1080       $export_link = "index.php?mode=export";
1081       $slideshow_link = "index.php?mode=slideshow";
1082
1083       $this->tmpl->assign('extern_link', $extern_link);
1084       $this->tmpl->assign('slideshow_link', $slideshow_link);
1085       $this->tmpl->assign('export_link', $export_link);
1086       $this->tmpl->assign('rss_link', $rss_link);
1087       $this->tmpl->assign('count', $count);
1088       $this->tmpl->assign('width', $this->cfg->thumb_width);
1089       $this->tmpl->assign('images', $images);
1090       $this->tmpl->assign('img_width', $img_width);
1091       $this->tmpl->assign('img_height', $img_height);
1092       $this->tmpl->assign('img_id', $img_id);
1093       $this->tmpl->assign('img_name', $img_name);
1094       $this->tmpl->assign('img_title', $img_title);
1095       $this->tmpl->assign('rows', $rows);
1096       $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1097
1098       $this->tmpl->show("photo_index.tpl");
1099
1100       if(isset($anchor))
1101          print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1102
1103    } // showPhotoIndex()
1104
1105    /**
1106     * show credit template
1107     */
1108    public function showCredits()
1109    {
1110       $this->tmpl->assign('version', $this->cfg->version);
1111       $this->tmpl->assign('product', $this->cfg->product);
1112       $this->tmpl->assign('db_version', $this->dbver);
1113       $this->tmpl->show("credits.tpl");
1114
1115    } // showCredits()
1116
1117    /**
1118     * create_thumbnails for the requested width
1119     *
1120     * this function creates image thumbnails of $orig_image
1121     * stored as $thumb_image. It will check if the image is
1122     * in a supported format, if necessary rotate the image
1123     * (based on EXIF orientation meta headers) and re-sizing.
1124     */
1125    public function create_thumbnail($orig_image, $thumb_image, $width)
1126    {  
1127       if(!file_exists($orig_image)) {
1128          return false;
1129       }
1130
1131       $details = getimagesize($orig_image);
1132       
1133       /* check if original photo is a support image type */
1134       if(!$this->checkifImageSupported($details['mime']))
1135          return false;
1136
1137       $meta = $this->get_meta_informations($orig_image);
1138
1139       $rotate = 0;
1140       $flip = false;
1141
1142       switch($meta['Orientation']) {
1143
1144          case 1: /* top, left */
1145             $rotate = 0; $flip = false; break;
1146          case 2: /* top, right */
1147             $rotate = 0; $flip = true; break;
1148          case 3: /* bottom, left */
1149             $rotate = 180; $flip = false; break;
1150          case 4: /* bottom, right */
1151             $rotate = 180; $flip = true; break;
1152          case 5: /* left side, top */
1153             $rotate = 90; $flip = true; break;
1154          case 6: /* right side, top */
1155             $rotate = 90; $flip = false; break;
1156          case 7: /* left side, bottom */
1157             $rotate = 270; $flip = true; break;
1158          case 8: /* right side, bottom */
1159             $rotate = 270; $flip = false; break;
1160       }
1161
1162       $src_img = @imagecreatefromjpeg($orig_image);
1163
1164       if(!$src_img) {
1165          print "Can't load image from ". $orig_image ."\n";
1166          return false;
1167       }
1168
1169       /* grabs the height and width */
1170       $cur_width = imagesx($src_img);
1171       $cur_height = imagesy($src_img);
1172
1173       // If requested width is more then the actual image width,
1174       // do not generate a thumbnail, instead safe the original
1175       // as thumbnail but with lower quality
1176
1177       if($width >= $cur_width) {
1178          $result = imagejpeg($src_img, $thumb_image, 75);
1179          imagedestroy($src_img);
1180          return true;
1181       }
1182
1183       // If the image will be rotate because EXIF orientation said so
1184       // 'virtually rotate' the image for further calculations
1185       if($rotate == 90 || $rotate == 270) {
1186          $tmp = $cur_width;
1187          $cur_width = $cur_height;
1188          $cur_height = $tmp;
1189       }
1190
1191       /* calculates aspect ratio */
1192       $aspect_ratio = $cur_height / $cur_width;
1193
1194       /* sets new size */
1195       if($aspect_ratio < 1) {
1196          $new_w = $width;
1197          $new_h = abs($new_w * $aspect_ratio);
1198       } else {
1199          /* 'virtually' rotate the image and calculate it's ratio */
1200          $tmp_w = $cur_height;
1201          $tmp_h = $cur_width;
1202          /* now get the ratio from the 'rotated' image */
1203          $tmp_ratio = $tmp_h/$tmp_w;
1204          /* now calculate the new dimensions */
1205          $tmp_w = $width;
1206          $tmp_h = abs($tmp_w * $tmp_ratio);
1207
1208          // now that we know, how high they photo should be, if it
1209          // gets rotated, use this high to scale the image
1210          $new_h = $tmp_h;
1211          $new_w = abs($new_h / $aspect_ratio);
1212
1213          // If the image will be rotate because EXIF orientation said so
1214          // now 'virtually rotate' back the image for the image manipulation
1215          if($rotate == 90 || $rotate == 270) {
1216             $tmp = $new_w;
1217             $new_w = $new_h;
1218             $new_h = $tmp;
1219          }
1220       }
1221
1222       /* creates new image of that size */
1223       $dst_img = imagecreatetruecolor($new_w, $new_h);
1224
1225       imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1226
1227       /* copies resized portion of original image into new image */
1228       imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1229
1230       /* needs the image to be flipped horizontal? */
1231       if($flip) {
1232          print "(FLIP)";
1233          $image = $dst_img;
1234          for($x = 0; $x < $new_w; $x++) {
1235             imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1236          }
1237       }
1238
1239       if($rotate) {
1240          $this->_debug("(ROTATE)");
1241          $dst_img = $this->rotateImage($dst_img, $rotate);
1242       }
1243
1244       /* write down new generated file */
1245       $result = imagejpeg($dst_img, $thumb_image, 75);
1246
1247       /* free your mind */
1248       imagedestroy($dst_img);
1249       imagedestroy($src_img);
1250
1251       if($result === false) {
1252          print "Can't write thumbnail ". $thumb_image ."\n";
1253          return false;
1254       }
1255
1256       return true;
1257
1258    } // create_thumbnail()
1259
1260    /**
1261     * return all exif meta data from the file
1262     */
1263    public function get_meta_informations($file)
1264    {
1265       return exif_read_data($file);
1266
1267    } // get_meta_informations()
1268
1269    /**
1270     * create phpfspot own sqlite database
1271     *
1272     * this function creates phpfspots own sqlite database
1273     * if it does not exist yet. this own is used to store
1274     * some necessary informations (md5 sum's, ...).
1275     */
1276    public function check_config_table()
1277    {
1278       // if the config table doesn't exist yet, create it
1279       if(!$this->cfg_db->db_check_table_exists("images")) {
1280          $this->cfg_db->db_exec("
1281             CREATE TABLE images (
1282                img_idx int primary key,
1283                img_md5 varchar(32)
1284             )
1285             ");
1286       }
1287
1288    } // check_config_table
1289
1290    /**
1291     * Generates a thumbnail from photo idx
1292     *
1293     * This function will generate JPEG thumbnails from provided F-Spot photo
1294     * indizes.
1295     *
1296     * 1. Check if all thumbnail generations (width) are already in place and
1297     *    readable
1298     * 2. Check if the md5sum of the original file has changed
1299     * 3. Generate the thumbnails if needed
1300     */
1301    public function gen_thumb($idx = 0, $force = 0)
1302    {
1303       $error = 0;
1304
1305       $resolutions = Array(
1306          $this->cfg->thumb_width,
1307          $this->cfg->photo_width,
1308          $this->cfg->mini_width,
1309       );
1310
1311       /* get details from F-Spot's database */
1312       $details = $this->get_photo_details($idx);
1313
1314       /* calculate file MD5 sum */
1315       $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1316
1317       if(!file_exists($full_path)) {
1318          $this->_error("File ". $full_path ." does not exist\n");
1319          return;
1320       }
1321
1322       if(!is_readable($full_path)) {
1323          $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1324          return;
1325       }
1326
1327       $file_md5 = md5_file($full_path);
1328
1329       $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1330
1331       $changes = false;
1332
1333       foreach($resolutions as $resolution) {
1334
1335          $thumb_sub_path = substr($file_md5, 0, 2);
1336          $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1337
1338          if(!file_exists(dirname($thumb_path))) {
1339             mkdir(dirname($thumb_path), 0755);
1340          }
1341
1342          /* if the thumbnail file doesn't exist, create it */
1343          if(!file_exists($thumb_path)) {
1344
1345             $this->_debug(" ". $resolution ."px");
1346             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1347                $error = 1;
1348
1349             $changes = true;
1350          }
1351          /* if the file hasn't changed there is no need to regen the thumb */
1352          elseif($file_md5 != $this->getMD5($idx) || $force) {
1353
1354             $this->_debug(" ". $resolution ."px");
1355             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1356                $error = 1;
1357
1358             $changes = true;
1359          }
1360       }
1361
1362       if(!$changes) {
1363          $this->_debug(" already exist");
1364       }
1365
1366       /* set the new/changed MD5 sum for the current photo */
1367       if(!$error) {
1368          $this->setMD5($idx, $file_md5);
1369       }
1370
1371       $this->_debug("\n");
1372
1373    } // gen_thumb()
1374
1375    /**
1376     * returns stored md5 sum for a specific photo
1377     *
1378     * this function queries the phpfspot database for a
1379     * stored MD5 checksum of the specified photo
1380     */
1381    public function getMD5($idx)
1382    {
1383       $result = $this->cfg_db->db_query("
1384          SELECT img_md5 
1385          FROM images
1386          WHERE img_idx='". $idx ."'
1387       ");
1388
1389       if(!$result)
1390          return 0;
1391
1392       $img = $this->cfg_db->db_fetch_object($result);
1393       return $img['img_md5'];
1394       
1395    } // getMD5()
1396
1397    /**
1398     * set MD5 sum for the specific photo
1399     */
1400    private function setMD5($idx, $md5)
1401    {
1402       $result = $this->cfg_db->db_exec("
1403          REPLACE INTO images (img_idx, img_md5)
1404          VALUES ('". $idx ."', '". $md5 ."')
1405       ");
1406
1407    } // setMD5()
1408
1409    /**
1410     * store current tag condition
1411     *
1412     * this function stores the current tag condition
1413     * (AND or OR) in the users session variables
1414     */
1415    public function setTagCondition($mode)
1416    {
1417       $_SESSION['tag_condition'] = $mode;
1418
1419    } // setTagCondition()
1420
1421    /** 
1422     * invoke tag & date search 
1423     *
1424     * this function will return all matching tags and store
1425     * them in the session variable selected_tags. furthermore
1426     * it also handles the date search.
1427     * getPhotoSelection() will then only return the matching
1428     * photos.
1429     */
1430    public function startSearch($searchfor, $from = 0, $to = 0)
1431    {
1432       $this->get_tags();
1433
1434       $_SESSION['searchfor'] = $searchfor;
1435       if($from != 0)
1436          $_SESSION['from_date'] = strtotime($from);
1437       else
1438          unset($_SESSION['from_date']);
1439       if($to != 0)
1440          $_SESSION['to_date'] = strtotime($to);
1441       else
1442          unset($_SESSION['to_date']);
1443
1444       if($searchfor != "") {
1445          /* new search, reset the current selected tags */
1446          $_SESSION['selected_tags'] = Array();
1447          foreach($this->avail_tags as $tag) {
1448             if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1449                array_push($_SESSION['selected_tags'], $tag);
1450          }
1451       }
1452
1453    } // startSearch()
1454
1455    /**
1456     * updates sort order in session variable
1457     *
1458     * this function is invoked by RPC and will sort the requested
1459     * sort order in the session variable.
1460     */
1461    public function updateSortOrder($order)
1462    {
1463       if(isset($this->sort_orders[$order])) {
1464          $_SESSION['sort_order'] = $order;
1465          return "ok";
1466       }
1467
1468       return "unkown error";
1469
1470    } // updateSortOrder()
1471
1472    /**
1473     * rotate image
1474     *
1475     * this function rotates the image according the
1476     * specified angel.
1477     */
1478    private function rotateImage($img, $degrees)
1479    {
1480       if(function_exists("imagerotate")) {
1481          $img = imagerotate($img, $degrees, 0);
1482       } else {
1483          function imagerotate($src_img, $angle)
1484          {
1485             $src_x = imagesx($src_img);
1486             $src_y = imagesy($src_img);
1487             if ($angle == 180) {
1488                $dest_x = $src_x;
1489                $dest_y = $src_y;
1490             }
1491             elseif ($src_x <= $src_y) {
1492                $dest_x = $src_y;
1493                $dest_y = $src_x;
1494             }
1495             elseif ($src_x >= $src_y) {
1496                $dest_x = $src_y;
1497                $dest_y = $src_x;
1498             }
1499                
1500             $rotate=imagecreatetruecolor($dest_x,$dest_y);
1501             imagealphablending($rotate, false);
1502                
1503             switch ($angle) {
1504             
1505                case 90:
1506                   for ($y = 0; $y < ($src_y); $y++) {
1507                      for ($x = 0; $x < ($src_x); $x++) {
1508                         $color = imagecolorat($src_img, $x, $y);
1509                         imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1510                      }
1511                   }
1512                   break;
1513
1514                case 270:
1515                   for ($y = 0; $y < ($src_y); $y++) {
1516                      for ($x = 0; $x < ($src_x); $x++) {
1517                         $color = imagecolorat($src_img, $x, $y);
1518                         imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1519                      }
1520                   }
1521                   break;
1522
1523                case 180:
1524                   for ($y = 0; $y < ($src_y); $y++) {
1525                      for ($x = 0; $x < ($src_x); $x++) {
1526                         $color = imagecolorat($src_img, $x, $y);
1527                         imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1528                      }
1529                   }
1530                   break;
1531
1532                default:
1533                   $rotate = $src_img;
1534                   break;
1535             };
1536
1537             return $rotate;
1538
1539          }
1540
1541          $img = imagerotate($img, $degrees);
1542
1543       }
1544
1545       return $img;
1546
1547    } // rotateImage()
1548
1549    /**
1550     * return all assigned tags for the specified photo
1551     */
1552    private function get_photo_tags($idx)
1553    {
1554       $result = $this->db->db_query("
1555          SELECT t.id, t.name
1556          FROM tags t
1557          INNER JOIN photo_tags pt
1558             ON t.id=pt.tag_id
1559          WHERE pt.photo_id='". $idx ."'
1560       ");
1561
1562       $tags = Array();
1563
1564       while($row = $this->db->db_fetch_object($result))
1565          $tags[$row['id']] = $row['name'];
1566
1567       return $tags;
1568
1569    } // get_photo_tags()
1570
1571    /**
1572     * create on-the-fly images with text within
1573     */
1574    public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1575    {
1576       if (strlen($color) != 6) 
1577          $color = 000000;
1578
1579       $int = hexdec($color);
1580       $h = imagefontheight($font);
1581       $fw = imagefontwidth($font);
1582       $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1583       $lines = count($txt);
1584       $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1585       $bg = imagecolorallocate($im, 255, 255, 255);
1586       $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1587       $y = 0;
1588
1589       foreach ($txt as $text) {
1590          $x = (($w - ($fw * strlen($text))) / 2);
1591          imagestring($im, $font, $x, $y, $text, $color);
1592          $y += ($h + $space);
1593       }
1594
1595       Header("Content-type: image/png");
1596       ImagePng($im);
1597
1598    } // showTextImage()
1599
1600    /**
1601     * check if all requirements are met
1602     */
1603    private function checkRequirements()
1604    {
1605       if(!function_exists("imagecreatefromjpeg")) {
1606          print "PHP GD library extension is missing<br />\n";
1607          $missing = true;
1608       }
1609
1610       if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1611          print "PHP SQLite3 library extension is missing<br />\n";
1612          $missing = true;
1613       }
1614
1615       /* Check for HTML_AJAX PEAR package, lent from Horde project */
1616       ini_set('track_errors', 1);
1617       @include_once 'HTML/AJAX/Server.php';
1618       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1619          print "PEAR HTML_AJAX package is missing<br />\n";
1620          $missing = true;
1621       }
1622       @include_once 'Calendar/Calendar.php';
1623       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1624          print "PEAR Calendar package is missing<br />\n";
1625          $missing = true;
1626       }
1627       ini_restore('track_errors');
1628
1629       if(isset($missing))
1630          return false;
1631
1632       return true;
1633
1634    } // checkRequirements()
1635
1636    private function _debug($text)
1637    {
1638       if($this->fromcmd) {
1639          print $text;
1640       }
1641
1642    } // _debug()
1643
1644    /**
1645     * check if specified MIME type is supported
1646     */
1647    public function checkifImageSupported($mime)
1648    {
1649       if(in_array($mime, Array("image/jpeg")))
1650          return true;
1651
1652       return false;
1653
1654    } // checkifImageSupported()
1655
1656    public function _error($text)
1657    {
1658       switch($this->cfg->logging) {
1659          case 'display':
1660             print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1661             print $text;
1662             break;
1663          case 'errorlog':  
1664             error_log($text);
1665             break;
1666          case 'logfile':
1667             error_log($text, 3, $his->cfg->log_file);
1668             break;
1669       }
1670
1671    } // _error()
1672
1673    /**
1674     * output calendard input fields
1675     */
1676    private function get_calendar($mode)
1677    {
1678       $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1679       $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1680       $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1681
1682       $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1683       if(!isset($_SESSION[$mode .'_date']))
1684          $output.= " disabled=\"disabled\"";
1685       $output.= " />\n";
1686       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1687       if(!isset($_SESSION[$mode .'_date']))
1688          $output.= " disabled=\"disabled\"";
1689       $output.= " />\n";
1690       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1691       if(!isset($_SESSION[$mode .'_date']))
1692          $output.= " disabled=\"disabled\"";
1693       $output.= " />\n";
1694
1695       return $output;
1696
1697    } // get_calendar()
1698
1699    /**
1700     * output calendar matrix
1701     */
1702    public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1703    {
1704       if (!isset($year)) $year = date('Y');
1705       if (!isset($month)) $month = date('m');
1706       if (!isset($day)) $day = date('d');
1707       $rows = 1;
1708       $cols = 1;
1709       $matrix = Array();
1710
1711       require_once CALENDAR_ROOT.'Month/Weekdays.php';
1712       require_once CALENDAR_ROOT.'Day.php';
1713
1714       // Build the month
1715       $month = new Calendar_Month_Weekdays($year,$month);
1716
1717       // Create links
1718       $prevStamp = $month->prevMonth(true);
1719       $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1720       $nextStamp = $month->nextMonth(true);
1721       $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1722
1723       $selectedDays = array (
1724          new Calendar_Day($year,$month,$day),
1725          new Calendar_Day($year,12,25),
1726       );
1727
1728       // Build the days in the month
1729       $month->build($selectedDays);
1730
1731       $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1732       $this->tmpl->assign('prev_month', $prev);
1733       $this->tmpl->assign('next_month', $next);
1734
1735       while ( $day = $month->fetch() ) {
1736    
1737          if(!isset($matrix[$rows]))
1738             $matrix[$rows] = Array();
1739
1740          $string = "";
1741
1742          $dayStamp = $day->thisDay(true);
1743          $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1744
1745          // isFirst() to find start of week
1746          if ( $day->isFirst() )
1747             $string.= "<tr>\n";
1748
1749          if ( $day->isSelected() ) {
1750             $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1751          } else if ( $day->isEmpty() ) {
1752             $string.= "<td>&nbsp;</td>\n";
1753          } else {
1754             $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1755          }
1756
1757          // isLast() to find end of week
1758          if ( $day->isLast() )
1759             $string.= "</tr>\n";
1760
1761          $matrix[$rows][$cols] = $string;
1762
1763          $cols++;
1764
1765          if($cols > 7) {
1766             $cols = 1;
1767             $rows++;
1768          }
1769       }
1770
1771       $this->tmpl->assign('matrix', $matrix);
1772       $this->tmpl->assign('rows', $rows);
1773       $this->tmpl->show("calendar.tpl");
1774
1775    } // get_calendar_matrix()
1776
1777    /**
1778     * output export page
1779     */
1780    public function getExport($mode)
1781    {
1782       $pictures = $this->getPhotoSelection();
1783       $current_tags = $this->getCurrentTags();  
1784
1785       foreach($pictures as $picture) {
1786
1787          $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1788          if($current_tags != "") {
1789             $orig_url.= "&tags=". $current_tags;
1790          } 
1791          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1792             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1793          }
1794
1795          $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1796
1797          switch($mode) {
1798
1799             case 'HTML':
1800                // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1801                print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1802                break;
1803                
1804             case 'MoinMoin':
1805                // "[%pictureurl% %thumbnailurl%]"
1806                print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1807                break;
1808
1809             case 'MoinMoinList':
1810                // " * [%pictureurl% %thumbnailurl%]"
1811                print "&nbsp;" . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1812                break;
1813          }
1814
1815       }
1816
1817    } // getExport()
1818
1819    /**
1820     * output RSS feed
1821     */
1822    public function getRSSFeed()
1823    {
1824       Header("Content-type: text/xml; charset=utf-8");
1825       print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1826 ?>
1827 <rss version="2.0"
1828    xmlns:media="http://search.yahoo.com/mrss/"
1829    xmlns:dc="http://purl.org/dc/elements/1.1/"
1830  >
1831  <channel>
1832   <title>phpfspot</title>
1833   <description>phpfspot RSS feed</description>
1834   <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1835   <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1836   <generator>phpfspot</generator>
1837 <?php
1838
1839       $pictures = $this->getPhotoSelection();
1840       $current_tags = $this->getCurrentTags();  
1841
1842       foreach($pictures as $picture) {
1843
1844          $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1845          if($current_tags != "") {
1846             $orig_url.= "&tags=". $current_tags;
1847          } 
1848          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1849             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1850          }
1851
1852          $details = $this->get_photo_details($picture);
1853
1854          $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1855          $thumb_html = htmlspecialchars("
1856 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1857 <br>
1858 ". $details['description']);
1859
1860          $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1861          $meta = $this->get_meta_informations($orig_path);
1862          $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1863
1864 ?>
1865   <item>
1866    <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1867    <link><?php print htmlspecialchars($orig_url); ?></link>
1868    <guid><?php print htmlspecialchars($orig_url); ?></guid>
1869    <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1870    <description>
1871     <?php print $thumb_html; ?> 
1872    </description>
1873    <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1874   </item>
1875 <?php
1876
1877       }
1878 ?>
1879  </channel>
1880 </rss>
1881 <?php
1882
1883
1884    } // getExport()
1885
1886  
1887    /**
1888     * return all selected tags as one string
1889     */
1890    private function getCurrentTags()
1891    {
1892       $current_tags = "";
1893       if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1894          foreach($_SESSION['selected_tags'] as $tag)
1895             $current_tags.= $tag .",";
1896          $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1897       }
1898       return $current_tags;
1899
1900    } // getCurrentTags()
1901
1902    /**
1903     * return the current photo
1904     */
1905    public function getCurrentPhoto()
1906    {
1907       if(isset($_SESSION['current_photo'])) {
1908          print $_SESSION['current_photo'];
1909       }
1910    } // getCurrentPhoto()
1911
1912    /**
1913     * tells the client browser what to do
1914     *
1915     * this function is getting called via AJAX by the
1916     * client browsers. it will tell them what they have
1917     * to do next. This is necessary for directly jumping
1918     * into photo index or single photo view when the are
1919     * requested with specific URLs
1920     */
1921    public function whatToDo()
1922    {
1923       if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1924          return "show_photo";
1925       }
1926       elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1927          return "showpi_tags";
1928       }
1929       elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1930          return "showpi";
1931       }
1932
1933       return "nothing special";
1934
1935    } // whatToDo()
1936
1937    /**
1938     * return the current process-user
1939     */
1940    private function getuid()
1941    {
1942       if($uid = posix_getuid()) {
1943          if($user = posix_getpwuid($uid)) {
1944             return $user['name'];
1945          }
1946       }
1947    
1948       return 'n/a';
1949    
1950    } // getuid()
1951
1952    /**
1953     * returns a select-dropdown box to select photo index sort parameters
1954     */
1955    public function smarty_sort_select_list($params, &$smarty)
1956    {
1957       $output = "";
1958
1959       foreach($this->sort_orders as $key => $value) {
1960          $output.= "<option value=\"". $key ."\"";
1961          if($key == $_SESSION['sort_order']) {
1962             $output.= " selected=\"selected\"";
1963          }
1964          $output.= ">". $value ."</option>";
1965       }
1966
1967       return $output;
1968
1969    } // smarty_sort_select_list()
1970
1971    /**
1972     * returns the currently selected sort order
1973     */ 
1974    private function get_sort_order()
1975    {
1976       switch($_SESSION['sort_order']) {
1977          case 'date_asc':
1978             return " ORDER BY p.time ASC";
1979             break;
1980          case 'date_desc':
1981             return " ORDER BY p.time DESC";
1982             break;
1983          case 'name_asc':
1984             if($this->dbver < 9) {
1985                return " ORDER BY p.name ASC";
1986             }
1987             else {
1988                return " ORDER BY basename(p.uri) ASC";
1989             }
1990             break;
1991          case 'name_desc':
1992             if($this->dbver < 9) {
1993                return " ORDER BY p.name DESC";
1994             }
1995             else {
1996                return " ORDER BY basename(p.uri) DESC";
1997             }
1998             break;
1999       }
2000
2001    } // get_sort_order()
2002
2003    /***
2004      * return the next to be shown slide show image
2005      *
2006      * this function returns the URL of the next image
2007      * in the slideshow sequence.
2008      */
2009    public function getNextSlideShowImage()
2010    {
2011       $all_photos = $this->getPhotoSelection();
2012
2013       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1) 
2014          $_SESSION['slideshow_img'] = 0;
2015       else
2016          $_SESSION['slideshow_img']++;
2017
2018       return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2019
2020    } // getNextSlideShowImage()
2021
2022    /***
2023      * return the previous to be shown slide show image
2024      *
2025      * this function returns the URL of the previous image
2026      * in the slideshow sequence.
2027      */
2028    public function getPrevSlideShowImage()
2029    {
2030       $all_photos = $this->getPhotoSelection();
2031
2032       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2033          $_SESSION['slideshow_img'] = 0;
2034       else
2035          $_SESSION['slideshow_img']--;
2036
2037       return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2038
2039    } // getPrevSlideShowImage()
2040
2041    public function resetSlideShow()
2042    {
2043       if(isset($_SESSION['slideshow_img']))
2044          unset($_SESSION['slideshow_img']);
2045    } // resetSlideShow()
2046    
2047    /***
2048      * get random photo
2049      *
2050      * this function will get all photos from the fspot
2051      * database and randomly return ONE entry
2052      *
2053      * saddly there is yet no sqlite3 function which returns
2054      * the bulk result in array, so we have to fill up our
2055      * own here.
2056      */ 
2057    public function get_random_photo()
2058    {
2059       $all = Array();
2060
2061       $result = $this->db->db_query("
2062          SELECT id
2063          FROM photos
2064       ");
2065       
2066       while($row = $this->db->db_fetch_object($result)) {
2067          array_push($all, $row['id']);
2068       }
2069
2070       return $all[array_rand($all)];
2071
2072    } // get_random_photo()
2073
2074    /**
2075     * validates provided date
2076     *
2077     * this function validates if the provided date
2078     * contains a valid date and will return true 
2079     * if it is.
2080     */
2081    public function isValidDate($date_str)
2082    {
2083       $timestamp = strtotime($date_str);
2084    
2085       if(is_numeric($timestamp))
2086          return true;
2087       
2088       return false;
2089
2090    } // isValidDate()
2091
2092    /**
2093     * timestamp to string conversion
2094     */
2095    private function ts2str($timestamp)
2096    {
2097       return strftime("%Y-%m-%d", $timestamp);
2098    } // ts2str()
2099
2100    private function extractTags($tags_str)
2101    {
2102       $not_validated = split(',', $_GET['tags']);
2103       $validated = array();
2104
2105       foreach($not_validated as $tag) {
2106          if(is_numeric($tag))
2107             array_push($validated, $tag);
2108       }
2109    
2110       return $validated;
2111    
2112    } // extractTags()
2113
2114    /**
2115     * returns the full path to a thumbnail
2116     */
2117    public function get_thumb_path($width, $photo)
2118    {
2119       $md5 = $this->getMD5($photo);
2120       $sub_path = substr($md5, 0, 2);
2121       return $this->cfg->thumb_path
2122          . "/"
2123          . $sub_path
2124          . "/"
2125          . $width
2126          . "_"
2127          . $md5;
2128
2129    } // get_thumb_path()
2130
2131    /**
2132     * returns server's virtual host name
2133     */
2134    private function get_server_name()
2135    {
2136       return $_SERVER['SERVER_NAME'];
2137    } // get_server_name()
2138
2139    /**
2140     * returns type of webprotocol which is
2141     * currently used
2142     */
2143    private function get_web_protocol()
2144    {
2145       if(!isset($_SERVER['HTTPS']))
2146          return "http";
2147       else
2148          return "https";
2149    } // get_web_protocol()
2150
2151    /**
2152     * return url to this phpfspot installation
2153     */
2154    private function get_phpfspot_url()
2155    {
2156       return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2157    } // get_phpfspot_url()
2158    
2159    /**
2160     * check file exists and is readable
2161     *
2162     * returns true, if everything is ok, otherwise false
2163     * if $silent is not set, this function will output and
2164     * error message
2165     */
2166    private function check_readable($file, $silent = null)
2167    {
2168       if(!file_exists($file)) {
2169          if(!isset($silent))
2170             print "File \"". $file ."\" does not exist.\n";
2171          return false;
2172       }
2173
2174       if(!is_readable($file)) {
2175          if(!isset($silent))
2176             print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2177          return false;
2178       }
2179
2180       return true;
2181
2182    } // check_readable()
2183
2184    /**
2185     * check if all needed indices are present
2186     *
2187     * this function checks, if some needed indices are already
2188     * present, or if not, create them on the fly. they are
2189     * necessary to speed up some queries like that one look for
2190     * all tags, when show_tags is specified in the configuration.
2191     */
2192    private function checkDbIndices()
2193    {
2194       $result = $this->db->db_exec("
2195          CREATE INDEX IF NOT EXISTS
2196             phototag
2197          ON
2198             photo_tags
2199                (photo_id, tag_id)
2200       ");
2201
2202    } // checkDbIndices()
2203
2204    /**
2205     * retrive F-Spot database version
2206     *
2207     * this function will return the F-Spot database version number
2208     * It is stored within the sqlite3 database in the table meta
2209     */
2210    public function getFspotDBVersion()
2211    {
2212       if($result = $this->db->db_fetchSingleRow("
2213          SELECT data as version
2214          FROM meta
2215          WHERE
2216             name LIKE 'F-Spot Database Version'
2217       "))
2218          return $result['version'];
2219
2220       return null;
2221
2222    } // getFspotDBVersion()
2223
2224    /**
2225     * parse the provided URI and will returned the
2226     * requested chunk
2227     */
2228    public function parse_uri($uri, $mode)
2229    {
2230       if(($components = parse_url($uri)) !== false) {
2231
2232          switch($mode) {
2233             case 'filename':
2234                return basename($components['path']);
2235                break;
2236             case 'dirname':
2237                return dirname($components['path']);
2238                break;
2239             case 'fullpath':
2240                return $components['path'];
2241                break;
2242          }
2243       }
2244
2245       return $uri;
2246
2247    } // parse_uri()
2248
2249 } // class PHPFSPOT
2250
2251 ?>