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