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