088e74d349590939b8f25343aa6ae850010fa8ba
[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.3";
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_title = Array();
987
988       for($i = $begin_with; $i < $end_with; $i++) {
989
990          if(isset($photos[$i])) {
991
992             $images[$thumbs] = $photos[$i];
993             $img_id[$thumbs] = $i;
994             $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
995             $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
996
997             $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
998
999             if(file_exists($thumb_path)) {
1000                $info = getimagesize($thumb_path); 
1001                $img_width[$thumbs] = $info[0];
1002                $img_height[$thumbs] = $info[1];
1003             }
1004             $thumbs++;
1005          } 
1006       }
1007
1008       // +1 for for smarty's selection iteration
1009       $thumbs++;
1010
1011       if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1012          $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1013
1014       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1015          $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1016          $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1017       }
1018
1019       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1020          $this->tmpl->assign('tag_result', 1);
1021       }
1022
1023       /* do we have to display the page selector ? */
1024       if($this->cfg->thumbs_per_page != 0) {
1025
1026          $page_select = "";
1027       
1028          /* calculate the page switchers */
1029          $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1030          $next_start = $begin_with + $this->cfg->thumbs_per_page;
1031
1032          if($begin_with != 0) 
1033             $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");"); 
1034          if($end_with < $count)
1035             $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");"); 
1036
1037          $photo_per_page  = $this->cfg->thumbs_per_page;
1038          $last_page = ceil($count / $photo_per_page);
1039
1040          /* get the current selected page */
1041          if($begin_with == 0) {
1042             $current_page = 1;
1043          } else {
1044             $current_page = 0;
1045             for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1046                $current_page++;
1047             }
1048          } 
1049
1050          $dotdot_made = 0;
1051
1052          for($i = 1; $i <= $last_page; $i++) {
1053
1054             if($current_page == $i)
1055                $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1056             elseif($current_page-1 == $i || $current_page+1 == $i)
1057                $style = "style=\"font-size: 105%;\"";
1058             elseif(($current_page-5 >= $i) && ($i != 1) ||
1059                ($current_page+5 <= $i) && ($i != $last_page))
1060                $style = "style=\"font-size: 75%;\"";
1061             else
1062                $style = "";
1063
1064             $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1065                if($style != "")
1066                   $select.= $style;
1067             $select.= ">". $i ."</a>&nbsp;";
1068
1069             // until 9 pages we show the selector from 1-9
1070             if($last_page <= 9) {
1071                $page_select.= $select;
1072                continue;
1073             } else {
1074                if($i == 1 /* first page */ || 
1075                   $i == $last_page /* last page */ ||
1076                   $i == $current_page /* current page */ ||
1077                   $i == ceil($last_page * 0.25) /* first quater */ ||
1078                   $i == ceil($last_page * 0.5) /* half */ ||
1079                   $i == ceil($last_page * 0.75) /* third quater */ ||
1080                   (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1081                   (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 */ ||
1082                   $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1083                   $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1084
1085                   $page_select.= $select;
1086                   $dotdot_made = 0;
1087                   continue;
1088
1089                }
1090             }
1091
1092             if(!$dotdot_made) {
1093                $page_select.= ".........&nbsp;";
1094                $dotdot_made = 1;
1095             }
1096          }
1097
1098          /* only show the page selector if we have more then one page */
1099          if($last_page > 1)
1100             $this->tmpl->assign('page_selector', $page_select);
1101       }
1102
1103       
1104       $current_tags = $this->getCurrentTags();
1105       $extern_link = "index.php?mode=showpi";
1106       $rss_link = "index.php?mode=rss";
1107       if($current_tags != "") {
1108          $extern_link.= "&tags=". $current_tags;
1109          $rss_link.= "&tags=". $current_tags;
1110       }
1111       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1112          $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1113          $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1114       }
1115
1116       $export_link = "index.php?mode=export";
1117       $slideshow_link = "index.php?mode=slideshow";
1118
1119       $this->tmpl->assign('extern_link', $extern_link);
1120       $this->tmpl->assign('slideshow_link', $slideshow_link);
1121       $this->tmpl->assign('export_link', $export_link);
1122       $this->tmpl->assign('rss_link', $rss_link);
1123       $this->tmpl->assign('count', $count);
1124       $this->tmpl->assign('width', $this->cfg->thumb_width);
1125       $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1126       $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1127       $this->tmpl->assign('images', $images);
1128       $this->tmpl->assign('img_width', $img_width);
1129       $this->tmpl->assign('img_height', $img_height);
1130       $this->tmpl->assign('img_id', $img_id);
1131       $this->tmpl->assign('img_name', $img_name);
1132       $this->tmpl->assign('img_title', $img_title);
1133       $this->tmpl->assign('thumbs', $thumbs);
1134
1135       $this->tmpl->show("photo_index.tpl");
1136
1137       /* if we are returning to photo index from an photo-view,
1138          scroll the window to the last shown photo-thumbnail.
1139          after this, unset the last_photo session variable.
1140       */
1141       if(isset($_SESSION['last_photo'])) {
1142          print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1143          unset($_SESSION['last_photo']);
1144       }
1145
1146    } // showPhotoIndex()
1147
1148    /**
1149     * show credit template
1150     */
1151    public function showCredits()
1152    {
1153       $this->tmpl->assign('version', $this->cfg->version);
1154       $this->tmpl->assign('product', $this->cfg->product);
1155       $this->tmpl->assign('db_version', $this->dbver);
1156       $this->tmpl->show("credits.tpl");
1157
1158    } // showCredits()
1159
1160    /**
1161     * create_thumbnails for the requested width
1162     *
1163     * this function creates image thumbnails of $orig_image
1164     * stored as $thumb_image. It will check if the image is
1165     * in a supported format, if necessary rotate the image
1166     * (based on EXIF orientation meta headers) and re-sizing.
1167     */
1168    public function create_thumbnail($orig_image, $thumb_image, $width)
1169    {  
1170       if(!file_exists($orig_image)) {
1171          return false;
1172       }
1173
1174       $details = getimagesize($orig_image);
1175       
1176       /* check if original photo is a support image type */
1177       if(!$this->checkifImageSupported($details['mime']))
1178          return false;
1179
1180       $meta = $this->get_meta_informations($orig_image);
1181
1182       $rotate = 0;
1183       $flip_hori = false;
1184       $flip_vert = false;
1185
1186       switch($meta['Orientation']) {
1187          case 1: /* top, left */
1188             /* nothing to do */ break;
1189          case 2: /* top, right */
1190             $rotate = 0; $flip_hori = true; break;
1191          case 3: /* bottom, left */
1192             $rotate = 180; break;
1193          case 4: /* bottom, right */
1194             $flip_vert = true; break;
1195          case 5: /* left side, top */
1196             $rotate = 90; $flip_vert = true; break;
1197          case 6: /* right side, top */
1198             $rotate = 90; break;
1199          case 7: /* left side, bottom */
1200             $rotate = 270; $flip_vert = true; break;
1201          case 8: /* right side, bottom */
1202             $rotate = 270; break;
1203       }
1204
1205       $src_img = @imagecreatefromjpeg($orig_image);
1206
1207       if(!$src_img) {
1208          print "Can't load image from ". $orig_image ."\n";
1209          return false;
1210       }
1211
1212       /* grabs the height and width */
1213       $cur_width = imagesx($src_img);
1214       $cur_height = imagesy($src_img);
1215
1216       // If requested width is more then the actual image width,
1217       // do not generate a thumbnail, instead safe the original
1218       // as thumbnail but with lower quality. But if the image
1219       // is to heigh too, then we still have to resize it.
1220       if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1221          $result = imagejpeg($src_img, $thumb_image, 75);
1222          imagedestroy($src_img);
1223          return true;
1224       }
1225
1226       // If the image will be rotate because EXIF orientation said so
1227       // 'virtually rotate' the image for further calculations
1228       if($rotate == 90 || $rotate == 270) {
1229          $tmp = $cur_width;
1230          $cur_width = $cur_height;
1231          $cur_height = $tmp;
1232       }
1233
1234       /* calculates aspect ratio */
1235       $aspect_ratio = $cur_height / $cur_width;
1236
1237       /* sets new size */
1238       if($aspect_ratio < 1) {
1239          $new_w = $width;
1240          $new_h = abs($new_w * $aspect_ratio);
1241       } else {
1242          /* 'virtually' rotate the image and calculate it's ratio */
1243          $tmp_w = $cur_height;
1244          $tmp_h = $cur_width;
1245          /* now get the ratio from the 'rotated' image */
1246          $tmp_ratio = $tmp_h/$tmp_w;
1247          /* now calculate the new dimensions */
1248          $tmp_w = $width;
1249          $tmp_h = abs($tmp_w * $tmp_ratio);
1250
1251          // now that we know, how high they photo should be, if it
1252          // gets rotated, use this high to scale the image
1253          $new_h = $tmp_h;
1254          $new_w = abs($new_h / $aspect_ratio);
1255
1256          // If the image will be rotate because EXIF orientation said so
1257          // now 'virtually rotate' back the image for the image manipulation
1258          if($rotate == 90 || $rotate == 270) {
1259             $tmp = $new_w;
1260             $new_w = $new_h;
1261             $new_h = $tmp;
1262          }
1263       }
1264
1265       /* creates new image of that size */
1266       $dst_img = imagecreatetruecolor($new_w, $new_h);
1267
1268       imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1269
1270       /* copies resized portion of original image into new image */
1271       imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1272
1273       /* needs the image to be flipped horizontal? */
1274       if($flip_hori) {
1275          $this->_debug("(FLIP)");
1276          $dst_img = $this->flipImage($dst_img, 'hori');
1277       }
1278       /* needs the image to be flipped vertical? */
1279       if($flip_vert) {
1280          $this->_debug("(FLIP)");
1281          $dst_img = $this->flipImage($dst_img, 'vert');
1282       }
1283
1284       if($rotate) {
1285          $this->_debug("(ROTATE)");
1286          $dst_img = $this->rotateImage($dst_img, $rotate);
1287       }
1288
1289       /* write down new generated file */
1290       $result = imagejpeg($dst_img, $thumb_image, 75);
1291
1292       /* free your mind */
1293       imagedestroy($dst_img);
1294       imagedestroy($src_img);
1295
1296       if($result === false) {
1297          print "Can't write thumbnail ". $thumb_image ."\n";
1298          return false;
1299       }
1300
1301       return true;
1302
1303    } // create_thumbnail()
1304
1305    /**
1306     * return all exif meta data from the file
1307     */
1308    public function get_meta_informations($file)
1309    {
1310       return exif_read_data($file);
1311
1312    } // get_meta_informations()
1313
1314    /**
1315     * create phpfspot own sqlite database
1316     *
1317     * this function creates phpfspots own sqlite database
1318     * if it does not exist yet. this own is used to store
1319     * some necessary informations (md5 sum's, ...).
1320     */
1321    public function check_config_table()
1322    {
1323       // if the config table doesn't exist yet, create it
1324       if(!$this->cfg_db->db_check_table_exists("images")) {
1325          $this->cfg_db->db_exec("
1326             CREATE TABLE images (
1327                img_idx int primary key,
1328                img_md5 varchar(32)
1329             )
1330             ");
1331       }
1332
1333    } // check_config_table
1334
1335    /**
1336     * Generates a thumbnail from photo idx
1337     *
1338     * This function will generate JPEG thumbnails from provided F-Spot photo
1339     * indizes.
1340     *
1341     * 1. Check if all thumbnail generations (width) are already in place and
1342     *    readable
1343     * 2. Check if the md5sum of the original file has changed
1344     * 3. Generate the thumbnails if needed
1345     */
1346    public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1347    {
1348       $error = 0;
1349
1350       $resolutions = Array(
1351          $this->cfg->thumb_width,
1352          $this->cfg->photo_width,
1353          $this->cfg->mini_width,
1354       );
1355
1356       /* get details from F-Spot's database */
1357       $details = $this->get_photo_details($idx);
1358
1359       /* calculate file MD5 sum */
1360       $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1361
1362       if(!file_exists($full_path)) {
1363          $this->_error("File ". $full_path ." does not exist\n");
1364          return;
1365       }
1366
1367       if(!is_readable($full_path)) {
1368          $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1369          return;
1370       }
1371
1372       $file_md5 = md5_file($full_path);
1373
1374       $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1375
1376       $changes = false;
1377
1378       foreach($resolutions as $resolution) {
1379    
1380          $generate_it = false;
1381
1382          $thumb_sub_path = substr($file_md5, 0, 2);
1383          $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1384
1385          /* if thumbnail-subdirectory does not exist yet, create it */
1386          if(!file_exists(dirname($thumb_path))) {
1387             mkdir(dirname($thumb_path), 0755);
1388          }
1389
1390          /* if the thumbnail file doesn't exist, create it */
1391          if(!file_exists($thumb_path)) {
1392             $generate_it = true;
1393          }
1394          /* if the file hasn't changed there is no need to regen the thumb */
1395          elseif($file_md5 != $this->getMD5($idx) || $force) {
1396             $generate_it = true;
1397          }
1398
1399          if($generate_it || $overwrite) {
1400
1401             $this->_debug(" ". $resolution ."px");
1402             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1403                $error = 1;
1404
1405             $changes = true;
1406          }
1407       }
1408
1409       if(!$changes) {
1410          $this->_debug(" already exist");
1411       }
1412
1413       /* set the new/changed MD5 sum for the current photo */
1414       if(!$error) {
1415          $this->setMD5($idx, $file_md5);
1416       }
1417
1418       $this->_debug("\n");
1419
1420    } // gen_thumb()
1421
1422    /**
1423     * returns stored md5 sum for a specific photo
1424     *
1425     * this function queries the phpfspot database for a
1426     * stored MD5 checksum of the specified photo
1427     */
1428    public function getMD5($idx)
1429    {
1430       $result = $this->cfg_db->db_query("
1431          SELECT img_md5 
1432          FROM images
1433          WHERE img_idx='". $idx ."'
1434       ");
1435
1436       if(!$result)
1437          return 0;
1438
1439       $img = $this->cfg_db->db_fetch_object($result);
1440       return $img['img_md5'];
1441       
1442    } // getMD5()
1443
1444    /**
1445     * set MD5 sum for the specific photo
1446     */
1447    private function setMD5($idx, $md5)
1448    {
1449       $result = $this->cfg_db->db_exec("
1450          REPLACE INTO images (img_idx, img_md5)
1451          VALUES ('". $idx ."', '". $md5 ."')
1452       ");
1453
1454    } // setMD5()
1455
1456    /**
1457     * store current tag condition
1458     *
1459     * this function stores the current tag condition
1460     * (AND or OR) in the users session variables
1461     */
1462    public function setTagCondition($mode)
1463    {
1464       $_SESSION['tag_condition'] = $mode;
1465
1466       return "ok";
1467
1468    } // setTagCondition()
1469
1470    /** 
1471     * invoke tag & date search 
1472     *
1473     * this function will return all matching tags and store
1474     * them in the session variable selected_tags. furthermore
1475     * it also handles the date search.
1476     * getPhotoSelection() will then only return the matching
1477     * photos.
1478     */
1479    public function startSearch()
1480    {
1481       if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1482          $from = $_POST['from'];
1483       }
1484       if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1485          $to = $_POST['to'];
1486       }
1487
1488       if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1489          $searchfor_tag = $_POST['for_tag'];
1490          $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1491       }
1492
1493       if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1494          $searchfor_name = $_POST['for_name'];
1495          $_SESSION['searchfor_name'] = $_POST['for_name'];
1496       }
1497
1498       $this->get_tags();
1499
1500       if(isset($from) && !empty($from))
1501          $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1502       else
1503          unset($_SESSION['from_date']);
1504
1505       if(isset($to) && !empty($to))
1506          $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1507       else
1508          unset($_SESSION['to_date']);
1509
1510       if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1511          /* new search, reset the current selected tags */
1512          $_SESSION['selected_tags'] = Array();
1513          foreach($this->avail_tags as $tag) {
1514             if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1515                array_push($_SESSION['selected_tags'], $tag);
1516          }
1517       }
1518
1519       return "ok";
1520
1521    } // startSearch()
1522
1523    /**
1524     * updates sort order in session variable
1525     *
1526     * this function is invoked by RPC and will sort the requested
1527     * sort order in the session variable.
1528     */
1529    public function updateSortOrder($order)
1530    {
1531       if(isset($this->sort_orders[$order])) {
1532          $_SESSION['sort_order'] = $order;
1533          return "ok";
1534       }
1535
1536       return "unkown error";
1537
1538    } // updateSortOrder()
1539
1540    /**
1541     * rotate image
1542     *
1543     * this function rotates the image according the
1544     * specified angel.
1545     */
1546    private function rotateImage($img, $degrees)
1547    {
1548       if(function_exists("imagerotate")) {
1549          $img = imagerotate($img, $degrees, 0);
1550       } else {
1551          function imagerotate($src_img, $angle)
1552          {
1553             $src_x = imagesx($src_img);
1554             $src_y = imagesy($src_img);
1555             if ($angle == 180) {
1556                $dest_x = $src_x;
1557                $dest_y = $src_y;
1558             }
1559             elseif ($src_x <= $src_y) {
1560                $dest_x = $src_y;
1561                $dest_y = $src_x;
1562             }
1563             elseif ($src_x >= $src_y) {
1564                $dest_x = $src_y;
1565                $dest_y = $src_x;
1566             }
1567                
1568             $rotate=imagecreatetruecolor($dest_x,$dest_y);
1569             imagealphablending($rotate, false);
1570                
1571             switch ($angle) {
1572             
1573                case 90:
1574                   for ($y = 0; $y < ($src_y); $y++) {
1575                      for ($x = 0; $x < ($src_x); $x++) {
1576                         $color = imagecolorat($src_img, $x, $y);
1577                         imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1578                      }
1579                   }
1580                   break;
1581
1582                case 270:
1583                   for ($y = 0; $y < ($src_y); $y++) {
1584                      for ($x = 0; $x < ($src_x); $x++) {
1585                         $color = imagecolorat($src_img, $x, $y);
1586                         imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1587                      }
1588                   }
1589                   break;
1590
1591                case 180:
1592                   for ($y = 0; $y < ($src_y); $y++) {
1593                      for ($x = 0; $x < ($src_x); $x++) {
1594                         $color = imagecolorat($src_img, $x, $y);
1595                         imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1596                      }
1597                   }
1598                   break;
1599
1600                default:
1601                   $rotate = $src_img;
1602                   break;
1603             };
1604
1605             return $rotate;
1606
1607          }
1608
1609          $img = imagerotate($img, $degrees);
1610
1611       }
1612
1613       return $img;
1614
1615    } // rotateImage()
1616
1617    /**
1618     * returns flipped image
1619     *
1620     * this function will return an either horizontal or
1621     * vertical flipped truecolor image.
1622     */
1623    private function flipImage($image, $mode)
1624    {
1625       $w = imagesx($image);
1626       $h = imagesy($image);
1627       $flipped = imagecreatetruecolor($w, $h);
1628
1629       switch($mode) {
1630          case 'vert':
1631             for ($y = 0; $y < $h; $y++) {
1632                imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1633             }
1634             break;
1635          case 'hori':
1636             for ($x = 0; $x < $w; $x++) {
1637                imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1638             }
1639             break;
1640       }
1641
1642       return $flipped;
1643
1644    } // flipImage()
1645
1646    /**
1647     * return all assigned tags for the specified photo
1648     */
1649    private function get_photo_tags($idx)
1650    {
1651       $result = $this->db->db_query("
1652          SELECT t.id, t.name
1653          FROM tags t
1654          INNER JOIN photo_tags pt
1655             ON t.id=pt.tag_id
1656          WHERE pt.photo_id='". $idx ."'
1657       ");
1658
1659       $tags = Array();
1660
1661       while($row = $this->db->db_fetch_object($result))
1662          $tags[$row['id']] = $row['name'];
1663
1664       return $tags;
1665
1666    } // get_photo_tags()
1667
1668    /**
1669     * create on-the-fly images with text within
1670     */
1671    public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1672    {
1673       if (strlen($color) != 6) 
1674          $color = 000000;
1675
1676       $int = hexdec($color);
1677       $h = imagefontheight($font);
1678       $fw = imagefontwidth($font);
1679       $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1680       $lines = count($txt);
1681       $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1682       $bg = imagecolorallocate($im, 255, 255, 255);
1683       $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1684       $y = 0;
1685
1686       foreach ($txt as $text) {
1687          $x = (($w - ($fw * strlen($text))) / 2);
1688          imagestring($im, $font, $x, $y, $text, $color);
1689          $y += ($h + $space);
1690       }
1691
1692       Header("Content-type: image/png");
1693       ImagePng($im);
1694
1695    } // showTextImage()
1696
1697    /**
1698     * check if all requirements are met
1699     */
1700    private function checkRequirements()
1701    {
1702       if(!function_exists("imagecreatefromjpeg")) {
1703          print "PHP GD library extension is missing<br />\n";
1704          $missing = true;
1705       }
1706
1707       if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1708          print "PHP SQLite3 library extension is missing<br />\n";
1709          $missing = true;
1710       }
1711
1712       /* Check for HTML_AJAX PEAR package, lent from Horde project */
1713       ini_set('track_errors', 1);
1714       @include_once 'HTML/AJAX/Server.php';
1715       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1716          print "PEAR HTML_AJAX package is missing<br />\n";
1717          $missing = true;
1718       }
1719       @include_once 'Calendar/Calendar.php';
1720       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1721          print "PEAR Calendar package is missing<br />\n";
1722          $missing = true;
1723       }
1724       @include_once 'Console/Getopt.php';
1725       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1726          print "PEAR Console_Getopt package is missing<br />\n";
1727          $missing = true;
1728       }
1729       ini_restore('track_errors');
1730
1731       if(isset($missing))
1732          return false;
1733
1734       return true;
1735
1736    } // checkRequirements()
1737
1738    private function _debug($text)
1739    {
1740       if($this->fromcmd) {
1741          print $text;
1742       }
1743
1744    } // _debug()
1745
1746    /**
1747     * check if specified MIME type is supported
1748     */
1749    public function checkifImageSupported($mime)
1750    {
1751       if(in_array($mime, Array("image/jpeg")))
1752          return true;
1753
1754       return false;
1755
1756    } // checkifImageSupported()
1757
1758    public function _error($text)
1759    {
1760       switch($this->cfg->logging) {
1761          default:
1762          case 'display':
1763             print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1764             print $text ."<br />\n";
1765             break;
1766          case 'errorlog':  
1767             error_log($text);
1768             break;
1769          case 'logfile':
1770             error_log($text, 3, $his->cfg->log_file);
1771             break;
1772       }
1773
1774       $this->runtime_error = true;
1775
1776    } // _error()
1777
1778    /**
1779     * output calendard input fields
1780     */
1781    private function get_calendar($mode)
1782    {
1783       $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1784       $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1785       $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1786
1787       $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1788       if(!isset($_SESSION[$mode .'_date']))
1789          $output.= " disabled=\"disabled\"";
1790       $output.= " />\n";
1791       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1792       if(!isset($_SESSION[$mode .'_date']))
1793          $output.= " disabled=\"disabled\"";
1794       $output.= " />\n";
1795       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1796       if(!isset($_SESSION[$mode .'_date']))
1797          $output.= " disabled=\"disabled\"";
1798       $output.= " />\n";
1799
1800       return $output;
1801
1802    } // get_calendar()
1803
1804    /**
1805     * output calendar matrix
1806     */
1807    public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1808    {
1809       if (!isset($year)) $year = date('Y');
1810       if (!isset($month)) $month = date('m');
1811       if (!isset($day)) $day = date('d');
1812       $rows = 1;
1813       $cols = 1;
1814       $matrix = Array();
1815
1816       require_once CALENDAR_ROOT.'Month/Weekdays.php';
1817       require_once CALENDAR_ROOT.'Day.php';
1818
1819       // Build the month
1820       $month = new Calendar_Month_Weekdays($year,$month);
1821
1822       // Create links
1823       $prevStamp = $month->prevMonth(true);
1824       $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1825       $nextStamp = $month->nextMonth(true);
1826       $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1827
1828       $selectedDays = array (
1829          new Calendar_Day($year,$month,$day),
1830          new Calendar_Day($year,12,25),
1831       );
1832
1833       // Build the days in the month
1834       $month->build($selectedDays);
1835
1836       $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1837       $this->tmpl->assign('prev_month', $prev);
1838       $this->tmpl->assign('next_month', $next);
1839
1840       while ( $day = $month->fetch() ) {
1841    
1842          if(!isset($matrix[$rows]))
1843             $matrix[$rows] = Array();
1844
1845          $string = "";
1846
1847          $dayStamp = $day->thisDay(true);
1848          $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1849
1850          // isFirst() to find start of week
1851          if ( $day->isFirst() )
1852             $string.= "<tr>\n";
1853
1854          if ( $day->isSelected() ) {
1855             $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1856          } else if ( $day->isEmpty() ) {
1857             $string.= "<td>&nbsp;</td>\n";
1858          } else {
1859             $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1860          }
1861
1862          // isLast() to find end of week
1863          if ( $day->isLast() )
1864             $string.= "</tr>\n";
1865
1866          $matrix[$rows][$cols] = $string;
1867
1868          $cols++;
1869
1870          if($cols > 7) {
1871             $cols = 1;
1872             $rows++;
1873          }
1874       }
1875
1876       $this->tmpl->assign('matrix', $matrix);
1877       $this->tmpl->assign('rows', $rows);
1878       $this->tmpl->show("calendar.tpl");
1879
1880    } // get_calendar_matrix()
1881
1882    /**
1883     * output export page
1884     */
1885    public function getExport($mode)
1886    {
1887       $pictures = $this->getPhotoSelection();
1888       $current_tags = $this->getCurrentTags();  
1889
1890       foreach($pictures as $picture) {
1891
1892          $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1893          if($current_tags != "") {
1894             $orig_url.= "&tags=". $current_tags;
1895          } 
1896          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1897             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1898          }
1899
1900          $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1901
1902          switch($mode) {
1903
1904             case 'HTML':
1905                // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1906                print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1907                break;
1908                
1909             case 'MoinMoin':
1910                // "[%pictureurl% %thumbnailurl%]"
1911                print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1912                break;
1913
1914             case 'MoinMoinList':
1915                // " * [%pictureurl% %thumbnailurl%]"
1916                print "&nbsp;" . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1917                break;
1918          }
1919
1920       }
1921
1922    } // getExport()
1923
1924    /**
1925     * output RSS feed
1926     */
1927    public function getRSSFeed()
1928    {
1929       Header("Content-type: text/xml; charset=utf-8");
1930       print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1931 ?>
1932 <rss version="2.0"
1933    xmlns:media="http://search.yahoo.com/mrss/"
1934    xmlns:dc="http://purl.org/dc/elements/1.1/"
1935  >
1936  <channel>
1937   <title>phpfspot</title>
1938   <description>phpfspot RSS feed</description>
1939   <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1940   <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1941   <generator>phpfspot</generator>
1942 <?php
1943
1944       $pictures = $this->getPhotoSelection();
1945       $current_tags = $this->getCurrentTags();  
1946
1947       foreach($pictures as $picture) {
1948
1949          $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1950          if($current_tags != "") {
1951             $orig_url.= "&tags=". $current_tags;
1952          } 
1953          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1954             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1955          }
1956
1957          $details = $this->get_photo_details($picture);
1958
1959          $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1960          $thumb_html = htmlspecialchars("
1961 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1962 <br>
1963 ". $details['description']);
1964
1965          $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1966          $meta = $this->get_meta_informations($orig_path);
1967          $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1968
1969 ?>
1970   <item>
1971    <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1972    <link><?php print htmlspecialchars($orig_url); ?></link>
1973    <guid><?php print htmlspecialchars($orig_url); ?></guid>
1974    <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1975    <description>
1976     <?php print $thumb_html; ?> 
1977    </description>
1978    <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1979   </item>
1980 <?php
1981
1982       }
1983 ?>
1984  </channel>
1985 </rss>
1986 <?php
1987
1988
1989    } // getExport()
1990
1991  
1992    /**
1993     * return all selected tags as one string
1994     */
1995    private function getCurrentTags()
1996    {
1997       $current_tags = "";
1998       if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1999          foreach($_SESSION['selected_tags'] as $tag)
2000             $current_tags.= $tag .",";
2001          $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2002       }
2003       return $current_tags;
2004
2005    } // getCurrentTags()
2006
2007    /**
2008     * return the current photo
2009     */
2010    public function getCurrentPhoto()
2011    {
2012       if(isset($_SESSION['current_photo'])) {
2013          print $_SESSION['current_photo'];
2014       }
2015    } // getCurrentPhoto()
2016
2017    /**
2018     * tells the client browser what to do
2019     *
2020     * this function is getting called via AJAX by the
2021     * client browsers. it will tell them what they have
2022     * to do next. This is necessary for directly jumping
2023     * into photo index or single photo view when the are
2024     * requested with specific URLs
2025     */
2026    public function whatToDo()
2027    {
2028       if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2029          return "show_photo";
2030       }
2031       elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2032          return "showpi_tags";
2033       }
2034       elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2035          return "showpi";
2036       }
2037
2038       return "nothing special";
2039
2040    } // whatToDo()
2041
2042    /**
2043     * return the current process-user
2044     */
2045    private function getuid()
2046    {
2047       if($uid = posix_getuid()) {
2048          if($user = posix_getpwuid($uid)) {
2049             return $user['name'];
2050          }
2051       }
2052    
2053       return 'n/a';
2054    
2055    } // getuid()
2056
2057    /**
2058     * returns a select-dropdown box to select photo index sort parameters
2059     */
2060    public function smarty_sort_select_list($params, &$smarty)
2061    {
2062       $output = "";
2063
2064       foreach($this->sort_orders as $key => $value) {
2065          $output.= "<option value=\"". $key ."\"";
2066          if($key == $_SESSION['sort_order']) {
2067             $output.= " selected=\"selected\"";
2068          }
2069          $output.= ">". $value ."</option>";
2070       }
2071
2072       return $output;
2073
2074    } // smarty_sort_select_list()
2075
2076    /**
2077     * returns the currently selected sort order
2078     */ 
2079    private function get_sort_order()
2080    {
2081       switch($_SESSION['sort_order']) {
2082          case 'date_asc':
2083             return " ORDER BY p.time ASC";
2084             break;
2085          case 'date_desc':
2086             return " ORDER BY p.time DESC";
2087             break;
2088          case 'name_asc':
2089             if($this->dbver < 9) {
2090                return " ORDER BY p.name ASC";
2091             }
2092             else {
2093                return " ORDER BY basename(p.uri) ASC";
2094             }
2095             break;
2096          case 'name_desc':
2097             if($this->dbver < 9) {
2098                return " ORDER BY p.name DESC";
2099             }
2100             else {
2101                return " ORDER BY basename(p.uri) DESC";
2102             }
2103             break;
2104          case 'tags_asc':
2105             return " ORDER BY t.name ASC ,p.time ASC";
2106             break;
2107          case 'tags_desc':
2108             return " ORDER BY t.name DESC ,p.time ASC";
2109             break;
2110       }
2111
2112    } // get_sort_order()
2113
2114    /***
2115      * return the next to be shown slide show image
2116      *
2117      * this function returns the URL of the next image
2118      * in the slideshow sequence.
2119      */
2120    public function getNextSlideShowImage()
2121    {
2122       $all_photos = $this->getPhotoSelection();
2123
2124       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1) 
2125          $_SESSION['slideshow_img'] = 0;
2126       else
2127          $_SESSION['slideshow_img']++;
2128
2129       return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2130
2131    } // getNextSlideShowImage()
2132
2133    /***
2134      * return the previous to be shown slide show image
2135      *
2136      * this function returns the URL of the previous image
2137      * in the slideshow sequence.
2138      */
2139    public function getPrevSlideShowImage()
2140    {
2141       $all_photos = $this->getPhotoSelection();
2142
2143       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2144          $_SESSION['slideshow_img'] = 0;
2145       else
2146          $_SESSION['slideshow_img']--;
2147
2148       return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2149
2150    } // getPrevSlideShowImage()
2151
2152    public function resetSlideShow()
2153    {
2154       if(isset($_SESSION['slideshow_img']))
2155          unset($_SESSION['slideshow_img']);
2156
2157    } // resetSlideShow()
2158    
2159    /***
2160      * get random photo
2161      *
2162      * this function will get all photos from the fspot
2163      * database and randomly return ONE entry
2164      *
2165      * saddly there is yet no sqlite3 function which returns
2166      * the bulk result in array, so we have to fill up our
2167      * own here.
2168      */ 
2169    public function get_random_photo()
2170    {
2171       $all = Array();
2172
2173       $result = $this->db->db_query("
2174          SELECT id
2175          FROM photos
2176       ");
2177       
2178       while($row = $this->db->db_fetch_object($result)) {
2179          array_push($all, $row['id']);
2180       }
2181
2182       return $all[array_rand($all)];
2183
2184    } // get_random_photo()
2185
2186    /**
2187     * validates provided date
2188     *
2189     * this function validates if the provided date
2190     * contains a valid date and will return true 
2191     * if it is.
2192     */
2193    public function isValidDate($date_str)
2194    {
2195       $timestamp = strtotime($date_str);
2196    
2197       if(is_numeric($timestamp))
2198          return true;
2199       
2200       return false;
2201
2202    } // isValidDate()
2203
2204    /**
2205     * timestamp to string conversion
2206     */
2207    private function ts2str($timestamp)
2208    {
2209       return strftime("%Y-%m-%d", $timestamp);
2210    } // ts2str()
2211
2212    private function extractTags($tags_str)
2213    {
2214       $not_validated = split(',', $_GET['tags']);
2215       $validated = array();
2216
2217       foreach($not_validated as $tag) {
2218          if(is_numeric($tag))
2219             array_push($validated, $tag);
2220       }
2221    
2222       return $validated;
2223    
2224    } // extractTags()
2225
2226    /**
2227     * returns the full path to a thumbnail
2228     */
2229    public function get_thumb_path($width, $photo)
2230    {
2231       $md5 = $this->getMD5($photo);
2232       $sub_path = substr($md5, 0, 2);
2233       return $this->cfg->thumb_path
2234          . "/"
2235          . $sub_path
2236          . "/"
2237          . $width
2238          . "_"
2239          . $md5;
2240
2241    } // get_thumb_path()
2242
2243    /**
2244     * returns server's virtual host name
2245     */
2246    private function get_server_name()
2247    {
2248       return $_SERVER['SERVER_NAME'];
2249    } // get_server_name()
2250
2251    /**
2252     * returns type of webprotocol which is
2253     * currently used
2254     */
2255    private function get_web_protocol()
2256    {
2257       if(!isset($_SERVER['HTTPS']))
2258          return "http";
2259       else
2260          return "https";
2261    } // get_web_protocol()
2262
2263    /**
2264     * return url to this phpfspot installation
2265     */
2266    private function get_phpfspot_url()
2267    {
2268       return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2269    } // get_phpfspot_url()
2270    
2271    /**
2272     * check file exists and is readable
2273     *
2274     * returns true, if everything is ok, otherwise false
2275     * if $silent is not set, this function will output and
2276     * error message
2277     */
2278    private function check_readable($file, $silent = null)
2279    {
2280       if(!file_exists($file)) {
2281          if(!isset($silent))
2282             print "File \"". $file ."\" does not exist.\n";
2283          return false;
2284       }
2285
2286       if(!is_readable($file)) {
2287          if(!isset($silent))
2288             print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2289          return false;
2290       }
2291
2292       return true;
2293
2294    } // check_readable()
2295
2296    /**
2297     * check if all needed indices are present
2298     *
2299     * this function checks, if some needed indices are already
2300     * present, or if not, create them on the fly. they are
2301     * necessary to speed up some queries like that one look for
2302     * all tags, when show_tags is specified in the configuration.
2303     */
2304    private function checkDbIndices()
2305    {
2306       $result = $this->db->db_exec("
2307          CREATE INDEX IF NOT EXISTS
2308             phototag
2309          ON
2310             photo_tags
2311                (photo_id, tag_id)
2312       ");
2313
2314    } // checkDbIndices()
2315
2316    /**
2317     * retrive F-Spot database version
2318     *
2319     * this function will return the F-Spot database version number
2320     * It is stored within the sqlite3 database in the table meta
2321     */
2322    public function getFspotDBVersion()
2323    {
2324       if($result = $this->db->db_fetchSingleRow("
2325          SELECT data as version
2326          FROM meta
2327          WHERE
2328             name LIKE 'F-Spot Database Version'
2329       "))
2330          return $result['version'];
2331
2332       return null;
2333
2334    } // getFspotDBVersion()
2335
2336    /**
2337     * parse the provided URI and will returned the
2338     * requested chunk
2339     */
2340    public function parse_uri($uri, $mode)
2341    {
2342       if(($components = parse_url($uri)) !== false) {
2343
2344          switch($mode) {
2345             case 'filename':
2346                return basename($components['path']);
2347                break;
2348             case 'dirname':
2349                return dirname($components['path']);
2350                break;
2351             case 'fullpath':
2352                return $components['path'];
2353                break;
2354          }
2355       }
2356
2357       return $uri;
2358
2359    } // parse_uri()
2360
2361    /**
2362     * validate config options
2363     *
2364     * this function checks if all necessary configuration options are
2365     * specified and set.
2366     */
2367    private function check_config_options()
2368    {
2369       if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2370          $this->_error("Please set \$page_title in phpfspot_cfg");
2371
2372       if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2373          $this->_error("Please set \$base_path in phpfspot_cfg");
2374
2375       if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2376          $this->_error("Please set \$web_path in phpfspot_cfg");
2377
2378       if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2379          $this->_error("Please set \$thumb_path in phpfspot_cfg");
2380
2381       if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2382          $this->_error("Please set \$smarty_path in phpfspot_cfg");
2383
2384       if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2385          $this->_error("Please set \$fspot_db in phpfspot_cfg");
2386
2387       if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2388          $this->_error("Please set \$db_access in phpfspot_cfg");
2389
2390       if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2391          $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2392
2393       if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2394          $this->_error("Please set \$thumb_width in phpfspot_cfg");
2395
2396       if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2397          $this->_error("Please set \$thumb_height in phpfspot_cfg");
2398
2399       if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2400          $this->_error("Please set \$photo_width in phpfspot_cfg");
2401
2402       if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2403          $this->_error("Please set \$mini_width in phpfspot_cfg");
2404
2405       if(!isset($this->cfg->thumbs_per_page))
2406          $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2407
2408       if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2409          $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2410
2411       if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2412          $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2413
2414       if(!isset($this->cfg->hide_tags))
2415          $this->_error("Please set \$hide_tags in phpfspot_cfg");
2416
2417       if(!isset($this->cfg->theme_name))
2418          $this->_error("Please set \$theme_name in phpfspot_cfg");
2419
2420       if(!isset($this->cfg->logging))
2421          $this->_error("Please set \$logging in phpfspot_cfg");
2422
2423       if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2424
2425          if(!isset($this->cfg->log_file))
2426             $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2427
2428          if(!is_writeable($this->cfg->log_file))
2429             $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2430
2431       }
2432
2433       /* check for pending slash on web_path */
2434       if(!preg_match("/\/$/", $this->cfg->web_path))
2435          $this->cfg->web_path.= "/";
2436
2437       return $this->runtime_error;
2438
2439    } // check_config_options()
2440
2441    /**
2442     * cleanup phpfspot own database
2443     *
2444     * When photos are getting delete from F-Spot, there will remain
2445     * remain some residues in phpfspot own database. This function
2446     * will try to wipe them out.
2447     */
2448    public function cleanup_phpfspot_db()
2449    {
2450       $to_delete = Array();
2451
2452       $result = $this->cfg_db->db_query("
2453          SELECT img_idx
2454          FROM images
2455          ORDER BY img_idx ASC
2456       ");
2457
2458       while($row = $this->cfg_db->db_fetch_object($result)) {
2459          if(!$this->db->db_fetchSingleRow("
2460             SELECT id
2461             FROM photos
2462             WHERE id='". $row['img_idx'] ."'")) {
2463
2464             array_push($to_delete, $row['img_idx'], ',');
2465          }
2466       }
2467
2468       print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2469
2470       $this->cfg_db->db_exec("
2471          DELETE FROM images
2472          WHERE img_idx IN (". implode($to_delete) .")
2473       ");
2474
2475    } // cleanup_phpfspot_db()
2476
2477    /**
2478     * return first image of the page, the $current photo
2479     * lies in.
2480     *
2481     * this function is used to find out the first photo of the
2482     * current page, in which the $current photo lies. this is
2483     * used to display the correct photo, when calling showPhotoIndex()
2484     * from showImage()
2485     */
2486    private function getCurrentPage($current, $max)
2487    {
2488       if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
2489          for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
2490             if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
2491                return $page_start;
2492          }
2493       }
2494       return 0;
2495
2496    } // getCurrentPage()
2497
2498 } // class PHPFSPOT
2499
2500 ?>