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