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