issue67, when called from an external link, also check the consider-date-search-box...
[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 require_once "phpfspot_tmpl.php";
27
28 class PHPFSPOT {
29
30    var $cfg;
31    var $db;
32    var $cfg_db;
33    var $tmpl;
34    var $tags;
35    var $avail_tags;
36
37    /**
38     * class constructor
39     *
40     * this function will be called on class construct
41     * and will check requirements, loads configuration,
42     * open databases and start the user session
43     */
44    public function __construct()
45    {
46       /* Check necessary requirements */
47       if(!$this->checkRequirements()) {
48          exit(1);
49       }
50
51       $this->cfg = new PHPFSPOT_CFG;
52
53       $this->db  = new PHPFSPOT_DB(&$this, $this->cfg->fspot_db);
54       
55       if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
56          print dirname($this->cfg->phpfspot_db) .": directory is not writeable!";
57          exit(1);
58       }
59          
60       $this->cfg_db = new PHPFSPOT_DB(&$this, $this->cfg->phpfspot_db);
61       if(!is_writeable($this->cfg->phpfspot_db)) {
62          print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
63          exit(1);
64       }
65       $this->check_config_table();
66
67
68       $this->tmpl = new PHPFSPOT_TMPL($this);
69
70       $this->get_tags();
71
72       session_start();
73
74       if(!isset($_SESSION['tag_condition']))
75          $_SESSION['tag_condition'] = 'or';
76
77       if(!isset($_SESSION['sort_order']))
78          $_SESSION['sort_order'] = 'date_asc';
79
80       if(!isset($_SESSION['searchfor']))
81          $_SESSION['searchfor'] = '';
82
83       // if begin_with is still set but rows_per_page is now 0, unset it
84       if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
85          unset($_SESSION['begin_with']);
86
87    } // __construct()
88
89    public function __destruct()
90    {
91
92    } // __destruct()
93
94    /**
95     * show - generate html output
96     *
97     * this function can be called after the constructor has
98     * prepared everyhing. it will load the index.tpl smarty
99     * template. if necessary it will registere pre-selects
100     * (photo index, photo, tag search, date search) into
101     * users session.
102     */
103    public function show()
104    {
105       $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
106       $this->tmpl->assign('page_title', $this->cfg->page_title);
107       $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
108       $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
109
110       $_SESSION['start_action'] = $_GET['mode'];
111
112       switch($_GET['mode']) {
113          case 'showpi':
114             if(isset($_GET['tags'])) {
115                $_SESSION['selected_tags'] = split(',', $_GET['tags']);
116             }
117             if(isset($_GET['from_date'])) {
118                $_SESSION['from_date'] = $_GET['from_date'];
119             }
120             if(isset($_GET['to_date'])) {
121                $_SESSION['to_date'] = $_GET['to_date'];
122             }
123             break;
124          case 'showp':
125             if(isset($_GET['tags'])) {
126                $_SESSION['selected_tags'] = split(',', $_GET['tags']);
127                $_SESSION['start_action'] = 'showp';
128             }
129             if(isset($_GET['id'])) {
130                $_SESSION['current_photo'] = $_GET['id'];
131                $_SESSION['start_action'] = 'showp';
132             }
133             if(isset($_GET['from_date'])) {
134                $_SESSION['from_date'] = $_GET['from_date'];
135             }
136             if(isset($_GET['to_date'])) {
137                $_SESSION['to_date'] = $_GET['to_date'];
138             }
139             break;
140          case 'export':
141             $this->tmpl->show("export.tpl");
142             return;
143             break;
144          case 'slideshow':
145             $this->tmpl->show("slideshow.tpl");
146             return;
147             break;
148       }
149
150       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
151        $this->tmpl->assign('date_search_enabled', true);
152
153       $this->tmpl->assign('from_date', $this->get_calendar('from'));
154       $this->tmpl->assign('to_date', $this->get_calendar('to'));
155       $this->tmpl->assign('sort_field', $this->get_sort_field());
156       $this->tmpl->assign('content_page', 'welcome.tpl');
157       $this->tmpl->show("index.tpl");
158
159
160    } // show()
161
162    /**
163     * get_tags - grab all tags of f-spot's database
164     *
165     * this function will get all available tags from
166     * the f-spot database and store them within two
167     * arrays within this clase for later usage. in
168     * fact, if the user requests (hide_tags) it will
169     * opt-out some of them.
170     *
171     * this function is getting called once by show()
172     */
173    private function get_tags()
174    {
175       $this->avail_tags = Array();
176       $count = 0;
177    
178       $result = $this->db->db_query("
179          SELECT id,name
180          FROM tags
181          ORDER BY sort_priority ASC
182       ");
183       
184       while($row = $this->db->db_fetch_object($result)) {
185
186          $tag_id = $row['id'];
187          $tag_name = $row['name'];
188
189          /* check if config requests to ignore this tag */
190          if(in_array($row['name'], $this->cfg->hide_tags))
191             continue;
192
193          $this->tags[$tag_id] = $tag_name; 
194          $this->avail_tags[$count] = $tag_id;
195
196          $count++;
197
198       }
199
200    } // get_tags()
201
202    /** 
203     * extract all photo details
204     * 
205     * retrieve all available details from f-spot's
206     * database and return them as object
207     */
208    public function get_photo_details($idx)
209    {
210       $result = $this->db->db_query("
211          SELECT *
212          FROM photos
213          WHERE id='". $idx ."'
214       ");
215       
216       return $this->db->db_fetch_object($result);
217
218    } // get_photo_details
219
220    /**
221     * returns aligned photo names 
222     *
223     * this function returns aligned (length) names for
224     * an specific photo. If the length of the name exceeds
225     * $limit the name will be shrinked (...)
226     */
227    public function getPhotoName($idx, $limit = 0)
228    {
229       if($details = $this->get_photo_details($idx)) {
230          $name = $details['name'];
231          if($limit != 0 && strlen($name) > $limit) {
232             $name = substr($name, 0, $limit-5) ."...". substr($name, -($limit-5));
233          }
234          return $name;
235       }
236
237    } // getPhotoName()
238
239    /**
240     * translate f-spoth photo path
241     * 
242     * as the full-qualified path recorded in the f-spot database
243     * is usally not the same as on the webserver, this function
244     * will replace the path with that one specified in the cfg
245     */
246    public function translate_path($path, $width = 0)
247    {  
248       return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
249
250    } // translate_path
251
252    /**
253     * control HTML ouput for a single photo
254     *
255     * this function provides all the necessary information
256     * for the single photo template.
257     */
258    public function showPhoto($photo)
259    {
260       /* get all photos from the current photo selection */
261       $all_photos = $this->getPhotoSelection();
262       $count = count($all_photos);
263
264       for($i = 0; $i < $count; $i++) {
265          
266          // $get_next will be set, when the photo which has to
267          // be displayed has been found - this means that the
268          // next available is in fact the NEXT image (for the
269          // navigation icons) 
270          if(isset($get_next)) {
271             $next_img = $all_photos[$i];
272             break;
273          }
274
275          /* the next photo is our NEXT photo */
276          if($all_photos[$i] == $photo) {
277             $get_next = 1;
278          }
279          else {
280             $previous_img = $all_photos[$i];
281          }
282
283          if($photo == $all_photos[$i]) {
284                $current = $i;
285          }
286       }
287
288       $details = $this->get_photo_details($photo);
289
290       if(!$details) {
291          print "error";
292          return;
293       }
294
295       $orig_path = $this->translate_path($details['directory_path']) ."/". $details['name'];
296       $thumb_path = $this->cfg->base_path ."/thumbs/". $this->cfg->photo_width ."_". $this->getMD5($photo);
297
298       if(!file_exists($orig_path)) {
299          $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
300       }
301
302       if(!is_readable($orig_path)) {
303          $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
304       }
305
306       /* If the thumbnail doesn't exist yet, try to create it */
307       if(!file_exists($thumb_path)) {
308          $this->gen_thumb($photo, true);
309          $thumb_path = $this->cfg->base_path ."/thumbs/". $this->cfg->photo_width ."_". $this->getMD5($photo);
310       }
311
312       /* get f-spot database meta information */
313       $meta = $this->get_meta_informations($orig_path);
314
315       /* If EXIF data are available, use them */
316       if(isset($meta['ExifImageWidth'])) {
317          $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
318       } else {
319          $info = getimagesize($orig_path);
320          $meta_res = $info[0] ."x". $info[1]; 
321       }
322
323       $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
324       $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
325       $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
326
327       $extern_link = "index.php?mode=showp&id=". $photo;
328       $current_tags = $this->getCurrentTags();
329       if($current_tags != "") {
330          $extern_link.= "&tags=". $current_tags;
331       }
332       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
333          $extern_link.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
334       }
335
336       $this->tmpl->assign('extern_link', $extern_link);
337
338       if(file_exists($thumb_path)) {
339
340          $info = getimagesize($thumb_path);
341
342          $this->tmpl->assign('description', $details['description']);
343          $this->tmpl->assign('image_name', $details['name']);
344
345          $this->tmpl->assign('width', $info[0]);
346          $this->tmpl->assign('height', $info[1]);
347          $this->tmpl->assign('ExifMadeOn', $meta_date);
348          $this->tmpl->assign('ExifMadeWith', $meta_make);
349          $this->tmpl->assign('ExifOrigResolution', $meta_res);
350          $this->tmpl->assign('ExifFileSize', $meta_size);
351     
352          $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&amp;width=". $this->cfg->photo_width);
353          $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
354
355          $this->tmpl->assign('tags', $this->get_photo_tags($photo));
356          $this->tmpl->assign('current', $current);
357       }
358       else {
359          $this->_error("Can't open file ". $thumb_path ."\n");
360          return;
361       }
362
363       if($previous_img) {
364          $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
365          $this->tmpl->assign('prev_img', $previous_img);
366       }
367
368       if($next_img) {
369          $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
370          $this->tmpl->assign('next_img', $next_img);
371       }
372       $this->tmpl->assign('mini_width', $this->cfg->mini_width);
373       $this->tmpl->assign('photo_number', $i);
374       $this->tmpl->assign('photo_count', count($all_photos));
375
376       $this->tmpl->show("single_photo.tpl");
377
378    } // showPhoto()
379
380    /**
381     * all available tags and tag cloud
382     *
383     * this function outputs all available tags (time ordered)
384     * and in addition output them as tag cloud (tags which have
385     * many photos will appears more then others)
386     */
387    public function getAvailableTags()
388    {
389       $output = "";
390
391       $result = $this->db->db_query("
392          SELECT tag_id as id, count(tag_id) as quantity
393          FROM photo_tags
394          INNER JOIN tags t
395             ON t.id = tag_id
396          GROUP BY tag_id
397          ORDER BY t.name ASC
398       ");
399
400       $tags = Array();
401
402       while($row = $this->db->db_fetch_object($result)) {
403          $tags[$row['id']] = $row['quantity'];
404       }
405
406       // change these font sizes if you will
407       $max_size = 125; // max font size in %
408       $min_size = 75; // min font size in %
409
410       // get the largest and smallest array values
411       $max_qty = max(array_values($tags));
412       $min_qty = min(array_values($tags));
413
414       // find the range of values
415       $spread = $max_qty - $min_qty;
416       if (0 == $spread) { // we don't want to divide by zero
417          $spread = 1;
418       }
419
420       // determine the font-size increment
421       // this is the increase per tag quantity (times used)
422       $step = ($max_size - $min_size)/($spread);
423
424       // loop through our tag array
425       foreach ($tags as $key => $value) {
426
427          if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
428             continue;
429
430           // calculate CSS font-size
431           // find the $value in excess of $min_qty
432           // multiply by the font-size increment ($size)
433           // and add the $min_size set above
434          $size = $min_size + (($value - $min_qty) * $step);
435           // uncomment if you want sizes in whole %:
436          $size = ceil($size);
437
438          $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
439
440       }
441
442       $output = substr($output, 0, strlen($output)-2);
443       print $output;
444
445    } // getAvailableTags()
446
447    /**
448     * output all selected tags
449     *
450     * this function output all tags which have been selected
451     * by the user. the selected tags are stored in the 
452     * session-variable $_SESSION['selected_tags']
453     */
454    public function getSelectedTags()
455    {
456       $output = "";
457       foreach($this->avail_tags as $tag)
458       {
459          // return all selected tags
460          if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
461             $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
462          }
463       }
464
465       $output = substr($output, 0, strlen($output)-2);
466       print $output;
467
468    } // getSelectedTags()
469
470    /**
471     * add tag to users session variable
472     *
473     * this function will add the specified to users current
474     * tag selection. if a date search has been made before
475     * it will be now cleared
476     */
477    public function addTag($tag)
478    {
479       if(!isset($_SESSION['selected_tags']))
480          $_SESSION['selected_tags'] = Array();
481
482       if(!in_array($tag, $_SESSION['selected_tags']))
483          array_push($_SESSION['selected_tags'], $tag);
484    
485    } // addTag()
486
487    /**
488     * remove tag to users session variable
489     *
490     * this function removes the specified tag from
491     * users current tag selection
492     */
493    public function delTag($tag)
494    {
495       if(isset($_SESSION['selected_tags'])) {
496          $key = array_search($tag, $_SESSION['selected_tags']);
497          unset($_SESSION['selected_tags'][$key]);
498          sort($_SESSION['selected_tags']);
499       }
500
501    } // delTag()
502
503    /**
504     * reset tag selection
505     *
506     * if there is any tag selection, it will be
507     * deleted now
508     */
509    public function resetTags()
510    {
511       if(isset($_SESSION['selected_tags']))
512          unset($_SESSION['selected_tags']);
513
514    } // resetTags()
515
516    /**
517     * reset single photo
518     *
519     * if a specific photo was requested (external link)
520     * unset the session variable now
521     */
522    public function resetPhotoView()
523    {
524       if(isset($_SESSION['current_photo']))
525          unset($_SESSION['current_photo']);
526
527    } // resetPhotoView();
528
529    /**
530     * reset tag search
531     *
532     * if any tag search has taken place, reset
533     * it now
534     */
535    public function resetTagSearch()
536    {
537       if(isset($_SESSION['searchfor']))
538          unset($_SESSION['searchfor']);
539
540    } // resetTagSearch()
541
542     /**
543     * reset date search
544     *
545     * if any date search has taken place, reset
546     * it now
547     */
548    public function resetDateSearch()
549    {
550       if(isset($_SESSION['from_date']))
551          unset($_SESSION['from_date']);
552       if(isset($_SESSION['to_date']))
553          unset($_SESSION['to_date']);
554
555    } // resetDateSearch();
556
557    /**
558     * return all photo according selection
559     *
560     * this function returns all photos based on
561     * the tag-selection, tag- or date-search.
562     * the tag-search also has to take care of AND
563     * and OR conjunctions
564     */
565    public function getPhotoSelection()
566    {  
567       $matched_photos = Array();
568
569       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
570          $from_date = strtotime($_SESSION['from_date'] ." 00:00:00");
571          $to_date = strtotime($_SESSION['to_date'] ." 23:59:59");
572          $additional_where_cond = "
573                p.time>='". $from_date ."'
574             AND
575                p.time<='". $to_date ."'
576          ";
577       } 
578
579       if(isset($_SESSION['sort_order'])) {
580          $order_str = $this->get_sort_order();
581       }
582
583       /* return a search result */
584       if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
585          $query_str = "
586             SELECT DISTINCT photo_id
587                FROM photo_tags pt
588             INNER JOIN photos p
589                ON p.id=pt.photo_id
590             INNER JOIN tags t
591                ON pt.tag_id=t.id
592             WHERE t.name LIKE '%". $_SESSION['searchfor'] ."%'";
593
594          if(isset($additional_where_cond))
595             $query_str.= "AND ". $additional_where_cond ." ";
596          if(isset($order_str))
597             $query_str.= $order_str;
598
599          $result = $this->db->db_query($query_str);
600          while($row = $this->db->db_fetch_object($result)) {
601             array_push($matched_photos, $row['photo_id']);
602          }
603          return $matched_photos;
604       }
605
606       /* return according the selected tags */
607       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
608          $selected = "";
609          foreach($_SESSION['selected_tags'] as $tag)
610             $selected.= $tag .",";
611          $selected = substr($selected, 0, strlen($selected)-1);
612
613          if($_SESSION['tag_condition'] == 'or') {
614             $query_str = "
615                SELECT DISTINCT photo_id
616                   FROM photo_tags pt
617                INNER JOIN photos p
618                   ON p.id=pt.photo_id
619                WHERE pt.tag_id IN (". $selected .")
620             ";
621             if(isset($additional_where_cond)) 
622                $query_str.= "AND ". $additional_where_cond ." ";
623             if(isset($order_str))
624                $query_str.= $order_str;
625          }
626          elseif($_SESSION['tag_condition'] == 'and') {
627
628             if(count($_SESSION['selected_tags']) >= 32) {
629                print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
630                print "evaluate your tag selection. Please remove some tags from your selection.\n";
631                return Array();
632             } 
633
634             /* Join together a table looking like
635
636                pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
637
638                so the query can quickly return all images matching the
639                selected tags in an AND condition
640
641             */
642
643             $query_str = "
644                SELECT DISTINCT pt1.photo_id
645                   FROM photo_tags pt1
646             ";
647
648             for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
649                $query_str.= "
650                   INNER JOIN photo_tags pt". ($i+2) ."
651                      ON pt1.photo_id=pt". ($i+2) .".photo_id
652                ";
653             }
654             $query_str.= "
655                INNER JOIN photos p
656                   ON pt1.photo_id=p.id
657             ";
658             $query_str.= "WHERE pt1.tag_id=". $_SESSION['selected_tags'][0];
659             for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
660                $query_str.= "
661                   AND pt". ($i+1) .".tag_id=". $_SESSION['selected_tags'][$i] ."
662                "; 
663             }
664             if(isset($additional_where_cond)) 
665                $query_str.= "AND ". $additional_where_cond;
666             if(isset($order_str))
667                $query_str.= $order_str;
668          }
669
670          $result = $this->db->db_query($query_str);
671          while($row = $this->db->db_fetch_object($result)) {
672             array_push($matched_photos, $row['photo_id']);
673          }
674          return $matched_photos;
675       }
676
677       /* return all available photos */
678       $query_str = "
679          SELECT DISTINCT photo_id
680             FROM photo_tags pt
681          INNER JOIN photos p
682             ON p.id=pt.photo_id
683       ";
684       if(isset($additional_where_cond)) 
685          $query_str.= "WHERE ". $additional_where_cond ." ";
686       if(isset($order_str))
687          $query_str.= $order_str;
688
689       $result = $this->db->db_query($query_str);
690       while($row = $this->db->db_fetch_object($result)) {
691          array_push($matched_photos, $row['photo_id']);
692       }
693       return $matched_photos;
694
695    } // getPhotoSelection()
696
697     /**
698     * control HTML ouput for photo index
699     *
700     * this function provides all the necessary information
701     * for the photo index template.
702     */
703    public function showPhotoIndex()
704    {
705       $photos = $this->getPhotoSelection();
706
707       $count = count($photos);
708
709       if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
710          $anchor = $_SESSION['begin_with'];
711
712       if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
713
714          $begin_with = 0;
715          $end_with = $count;
716
717       }
718       elseif($this->cfg->rows_per_page > 0) {
719
720          if(!$_SESSION['begin_with'] || $_SESSION['begin_with'] == 0)
721             $begin_with = 0;
722          else {
723
724             $begin_with = $_SESSION['begin_with'];
725
726             // verify $begin_with - perhaps the thumbs-per-rows or
727             // rows-per-page variables have changed or the jump back
728             // from a photo wasn't exact - so calculate the real new
729             // starting point
730             $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
731             for($i = 0; $i <= $count; $i+=$multiplicator) {
732                if($begin_with >= $i && $begin_with < $i+$multiplicator) {
733                   $begin_with = $i;
734                   break;
735                }
736             }
737          }
738
739          $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
740       }
741
742    
743       $rows = 0;
744       $cols = 0;
745       $images[$rows] = Array();
746       $img_height[$rows] = Array();
747       $img_width[$rows] = Array();
748       $img_id[$rows] = Array();
749       $img_name[$rows] = Array();
750       $img_title = Array();
751
752       for($i = $begin_with; $i < $end_with; $i++) {
753
754          $images[$rows][$cols] = $photos[$i];
755          $img_id[$rows][$cols] = $i;
756          $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
757          $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
758
759          $thumb_path = $this->cfg->base_path ."/thumbs/". $this->cfg->thumb_width ."_". $this->getMD5($photos[$i]);
760
761          if(file_exists($thumb_path)) {
762             $info = getimagesize($thumb_path); 
763             $img_width[$rows][$cols] = $info[0];
764             $img_height[$rows][$cols] = $info[1];
765          }
766
767          if($cols == $this->cfg->thumbs_per_row-1) {
768             $cols = 0;
769             $rows++;
770             $images[$rows] = Array();
771             $img_width[$rows] = Array();
772             $img_height[$rows] = Array();
773          }
774          else {
775             $cols++;
776          }
777       } 
778
779       // +1 for for smarty's selection iteration
780       $rows++;
781
782       if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
783          $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
784
785       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
786          $this->tmpl->assign('from_date', $_SESSION['from_date']);
787          $this->tmpl->assign('to_date', $_SESSION['to_date']);
788       }
789
790       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
791          $this->tmpl->assign('tag_result', 1);
792       }
793
794       /* do we have to display the page selector ? */
795       if($this->cfg->rows_per_page != 0) {
796       
797          /* calculate the page switchers */
798          $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
799          $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
800
801          if($begin_with != 0) 
802             $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");"); 
803          if($end_with < $count)
804             $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");"); 
805
806          $photo_per_page  = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
807          $last_page = ceil($count / $photo_per_page);
808
809          /* get the current selected page */
810          if($begin_with == 0) {
811             $current_page = 1;
812          } else {
813             $current_page = 0;
814             for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
815                $current_page++;
816             }
817          } 
818
819          $dotdot_made = 0;
820
821          for($i = 1; $i <= $last_page; $i++) {
822
823             if($current_page == $i)
824                $style = "style=\"font-size: 125%; text-decoration: underline;\"";
825             elseif($current_page-1 == $i || $current_page+1 == $i)
826                $style = "style=\"font-size: 105%;\"";
827             elseif(($current_page-5 >= $i) && ($i != 1) ||
828                ($current_page+5 <= $i) && ($i != $last_page))
829                $style = "style=\"font-size: 75%;\"";
830             else
831                $style = "";
832
833             $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
834                if($style != "")
835                   $select.= $style;
836             $select.= ">". $i ."</a>&nbsp;";
837
838             // until 9 pages we show the selector from 1-9
839             if($last_page <= 9) {
840                $page_select.= $select;
841                continue;
842             } else {
843                if($i == 1 /* first page */ || 
844                   $i == $last_page /* last page */ ||
845                   $i == $current_page /* current page */ ||
846                   $i == ceil($last_page * 0.25) /* first quater */ ||
847                   $i == ceil($last_page * 0.5) /* half */ ||
848                   $i == ceil($last_page * 0.75) /* third quater */ ||
849                   (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
850                   (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 */ ||
851                   $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
852                   $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
853
854                   $page_select.= $select;
855                   $dotdot_made = 0;
856                   continue;
857
858                }
859             }
860
861             if(!$dotdot_made) {
862                $page_select.= ".........&nbsp;";
863                $dotdot_made = 1;
864             }
865          }
866
867          /* only show the page selector if we have more then one page */
868          if($last_page > 1)
869             $this->tmpl->assign('page_selector', $page_select);
870       }
871
872       
873       $current_tags = $this->getCurrentTags();
874       $extern_link = "index.php?mode=showpi";
875       if($current_tags != "") {
876          $extern_link.= "&tags=". $current_tags;
877       }
878       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
879          $extern_link.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
880       }
881
882       $export_link = "index.php?mode=export";
883       $slideshow_link = "index.php?mode=slideshow";
884
885       $this->tmpl->assign('extern_link', $extern_link);
886       $this->tmpl->assign('slideshow_link', $slideshow_link);
887       $this->tmpl->assign('export_link', $export_link);
888       $this->tmpl->assign('count', $count);
889       $this->tmpl->assign('width', $this->cfg->thumb_width);
890       $this->tmpl->assign('images', $images);
891       $this->tmpl->assign('img_width', $img_width);
892       $this->tmpl->assign('img_height', $img_height);
893       $this->tmpl->assign('img_id', $img_id);
894       $this->tmpl->assign('img_name', $img_name);
895       $this->tmpl->assign('img_title', $img_title);
896       $this->tmpl->assign('rows', $rows);
897       $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
898
899       $this->tmpl->show("photo_index.tpl");
900
901       if(isset($anchor))
902          print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
903
904    } // showPhotoIndex()
905
906    /**
907     * show credit template
908     */
909    public function showCredits()
910    {
911       $this->tmpl->assign('version', $this->cfg->version);
912       $this->tmpl->assign('product', $this->cfg->product);
913       $this->tmpl->show("credits.tpl");
914
915    } // showCredits()
916
917    /**
918     * create_thumbnails for the requested width
919     *
920     * this function creates image thumbnails of $orig_image
921     * stored as $thumb_image. It will check if the image is
922     * in a supported format, if necessary rotate the image
923     * (based on EXIF orientation meta headers) and re-sizing.
924     */
925    public function create_thumbnail($orig_image, $thumb_image, $width)
926    {  
927       if(!file_exists($orig_image)) {
928          return false;
929       }
930
931       $details = getimagesize($orig_image);
932       
933       /* check if original photo is a support image type */
934       if(!$this->checkifImageSupported($details['mime']))
935          return false;
936
937       $meta = $this->get_meta_informations($orig_image);
938
939       $rotate = 0;
940       $flip = false;
941
942       switch($meta['Orientation']) {
943
944          case 1: /* top, left */
945             $rotate = 0; $flip = false; break;
946          case 2: /* top, right */
947             $rotate = 0; $flip = true; break;
948          case 3: /* bottom, left */
949             $rotate = 180; $flip = false; break;
950          case 4: /* bottom, right */
951             $rotate = 180; $flip = true; break;
952          case 5: /* left side, top */
953             $rotate = 90; $flip = true; break;
954          case 6: /* right side, top */
955             $rotate = 90; $flip = false; break;
956          case 7: /* left side, bottom */
957             $rotate = 270; $flip = true; break;
958          case 8: /* right side, bottom */
959             $rotate = 270; $flip = false; break;
960       }
961
962       $src_img = @imagecreatefromjpeg($orig_image);
963
964       if(!$src_img) {
965          print "Can't load image from ". $orig_image ."\n";
966          return false;
967       }
968
969       /* grabs the height and width */
970       $cur_width = imagesx($src_img);
971       $cur_height = imagesy($src_img);
972
973       // If requested width is more then the actual image width,
974       // do not generate a thumbnail, instead safe the original
975       // as thumbnail but with lower quality
976
977       if($width >= $cur_width) {
978          $result = imagejpeg($src_img, $thumb_image, 75);
979          imagedestroy($src_img);
980          return true;
981       }
982
983       // If the image will be rotate because EXIF orientation said so
984       // 'virtually rotate' the image for further calculations
985       if($rotate == 90 || $rotate == 270) {
986          $tmp = $cur_width;
987          $cur_width = $cur_height;
988          $cur_height = $tmp;
989       }
990
991       /* calculates aspect ratio */
992       $aspect_ratio = $cur_height / $cur_width;
993
994       /* sets new size */
995       if($aspect_ratio < 1) {
996          $new_w = $width;
997          $new_h = abs($new_w * $aspect_ratio);
998       } else {
999          /* 'virtually' rotate the image and calculate it's ratio */
1000          $tmp_w = $cur_height;
1001          $tmp_h = $cur_width;
1002          /* now get the ratio from the 'rotated' image */
1003          $tmp_ratio = $tmp_h/$tmp_w;
1004          /* now calculate the new dimensions */
1005          $tmp_w = $width;
1006          $tmp_h = abs($tmp_w * $tmp_ratio);
1007
1008          // now that we know, how high they photo should be, if it
1009          // gets rotated, use this high to scale the image
1010          $new_h = $tmp_h;
1011          $new_w = abs($new_h / $aspect_ratio);
1012
1013          // If the image will be rotate because EXIF orientation said so
1014          // now 'virtually rotate' back the image for the image manipulation
1015          if($rotate == 90 || $rotate == 270) {
1016             $tmp = $new_w;
1017             $new_w = $new_h;
1018             $new_h = $tmp;
1019          }
1020       }
1021
1022       /* creates new image of that size */
1023       $dst_img = imagecreatetruecolor($new_w, $new_h);
1024
1025       imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1026
1027       /* copies resized portion of original image into new image */
1028       imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1029
1030       /* needs the image to be flipped horizontal? */
1031       if($flip) {
1032          print "(FLIP)";
1033          $image = $dst_img;
1034          for($x = 0; $x < $new_w; $x++) {
1035             imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1036          }
1037       }
1038
1039       if($rotate) {
1040          $this->_debug("(ROTATE)");
1041          $dst_img = $this->rotateImage($dst_img, $rotate);
1042       }
1043
1044       /* write down new generated file */
1045       $result = imagejpeg($dst_img, $thumb_image, 75);
1046
1047       /* free your mind */
1048       imagedestroy($dst_img);
1049       imagedestroy($src_img);
1050
1051       if($result === false) {
1052          print "Can't write thumbnail ". $thumb_image ."\n";
1053          return false;
1054       }
1055
1056       return true;
1057
1058    } // create_thumbnail()
1059
1060    /**
1061     * return all exif meta data from the file
1062     */
1063    public function get_meta_informations($file)
1064    {
1065       return exif_read_data($file);
1066
1067    } // get_meta_informations()
1068
1069    /**
1070     * create phpfspot own sqlite database
1071     *
1072     * this function creates phpfspots own sqlite database
1073     * if it does not exist yet. this own is used to store
1074     * some necessary informations (md5 sum's, ...).
1075     */
1076    public function check_config_table()
1077    {
1078       // if the config table doesn't exist yet, create it
1079       if(!$this->cfg_db->db_check_table_exists("images")) {
1080          $this->cfg_db->db_exec("
1081             CREATE TABLE images (
1082                img_idx int primary key,
1083                img_md5 varchar(32)
1084             )
1085             ");
1086       }
1087
1088    } // check_config_table
1089
1090    /**
1091     * Generates a thumbnail from photo idx
1092     *
1093     * This function will generate JPEG thumbnails from provided F-Spot photo
1094     * indizes.
1095     *
1096     * 1. Check if all thumbnail generations (width) are already in place and
1097     *    readable
1098     * 2. Check if the md5sum of the original file has changed
1099     * 3. Generate the thumbnails if needed
1100     */
1101    public function gen_thumb($idx = 0, $force = 0)
1102    {
1103       $error = 0;
1104
1105       $resolutions = Array(
1106          $this->cfg->thumb_width,
1107          $this->cfg->photo_width,
1108          $this->cfg->mini_width,
1109       );
1110
1111       /* get details from F-Spot's database */
1112       $details = $this->get_photo_details($idx);
1113
1114       /* calculate file MD5 sum */
1115       $full_path = $this->translate_path($details['directory_path'])  ."/". $details['name'];
1116
1117       if(!file_exists($full_path)) {
1118          $this->_error("File ". $full_path ." does not exist\n");
1119          return;
1120       }
1121
1122       if(!is_readable($full_path)) {
1123          $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1124          return;
1125       }
1126
1127       $file_md5 = md5_file($full_path);
1128
1129       $this->_debug("Image [". $idx ."] ". $details['name'] ." Thumbnails:");
1130
1131       foreach($resolutions as $resolution) {
1132
1133          $thumb_path = $this->cfg->base_path ."/thumbs/". $resolution ."_". $file_md5;
1134
1135          /* if the thumbnail file doesn't exist, create it */
1136          if(!file_exists($thumb_path)) {
1137
1138             $this->_debug(" ". $resolution ."px");
1139             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1140                $error = 1;
1141          }
1142          /* if the file hasn't changed there is no need to regen the thumb */
1143          elseif($file_md5 != $this->getMD5($idx) || $force) {
1144
1145             $this->_debug(" ". $resolution ."px");
1146             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1147                $error = 1;
1148
1149          }
1150       }
1151
1152       /* set the new/changed MD5 sum for the current photo */
1153       if(!$error) {
1154          $this->setMD5($idx, $file_md5);
1155       }
1156
1157       $this->_debug("\n");
1158
1159    } // gen_thumb()
1160
1161    /**
1162     * returns stored md5 sum for a specific photo
1163     *
1164     * this function queries the phpfspot database for a
1165     * stored MD5 checksum of the specified photo
1166     */
1167    public function getMD5($idx)
1168    {
1169       $result = $this->cfg_db->db_query("
1170          SELECT img_md5 
1171          FROM images
1172          WHERE img_idx='". $idx ."'
1173       ");
1174
1175       if(!$result)
1176          return 0;
1177
1178       $img = $this->cfg_db->db_fetch_object($result);
1179       return $img['img_md5'];
1180       
1181    } // getMD5()
1182
1183    /**
1184     * set MD5 sum for the specific photo
1185     */
1186    private function setMD5($idx, $md5)
1187    {
1188       $result = $this->cfg_db->db_exec("
1189          REPLACE INTO images (img_idx, img_md5)
1190          VALUES ('". $idx ."', '". $md5 ."')
1191       ");
1192
1193    } // setMD5()
1194
1195    /**
1196     * store current tag condition
1197     *
1198     * this function stores the current tag condition
1199     * (AND or OR) in the users session variables
1200     */
1201    public function setTagCondition($mode)
1202    {
1203       $_SESSION['tag_condition'] = $mode;
1204
1205    } // setTagCondition()
1206
1207    /** 
1208     * invoke tag & date search 
1209     *
1210     * this function will return all matching tags and store
1211     * them in the session variable selected_tags. furthermore
1212     * it also handles the date search.
1213     * getPhotoSelection() will then only return the matching
1214     * photos.
1215     */
1216    public function startSearch($searchfor, $from, $to, $sort_order)
1217    {
1218       $_SESSION['searchfor'] = $searchfor;
1219       $_SESSION['from_date'] = $from;
1220       $_SESSION['to_date'] = $to;
1221       $_SESSION['sort_order'] = $sort_order;
1222
1223       if($searchfor != "") {
1224          /* new search, reset the current selected tags */
1225          $_SESSION['selected_tags'] = Array();
1226          foreach($this->avail_tags as $tag) {
1227             if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1228                array_push($_SESSION['selected_tags'], $tag);
1229          }
1230       }
1231    } // startSearch()
1232
1233    /**
1234     * rotate image
1235     *
1236     * this function rotates the image according the
1237     * specified angel.
1238     */
1239    private function rotateImage($img, $degrees)
1240    {
1241       if(function_exists("imagerotate")) {
1242          $img = imagerotate($img, $degrees, 0);
1243       } else {
1244          function imagerotate($src_img, $angle)
1245          {
1246             $src_x = imagesx($src_img);
1247             $src_y = imagesy($src_img);
1248             if ($angle == 180) {
1249                $dest_x = $src_x;
1250                $dest_y = $src_y;
1251             }
1252             elseif ($src_x <= $src_y) {
1253                $dest_x = $src_y;
1254                $dest_y = $src_x;
1255             }
1256             elseif ($src_x >= $src_y) {
1257                $dest_x = $src_y;
1258                $dest_y = $src_x;
1259             }
1260                
1261             $rotate=imagecreatetruecolor($dest_x,$dest_y);
1262             imagealphablending($rotate, false);
1263                
1264             switch ($angle) {
1265             
1266                case 90:
1267                   for ($y = 0; $y < ($src_y); $y++) {
1268                      for ($x = 0; $x < ($src_x); $x++) {
1269                         $color = imagecolorat($src_img, $x, $y);
1270                         imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1271                      }
1272                   }
1273                   break;
1274
1275                case 270:
1276                   for ($y = 0; $y < ($src_y); $y++) {
1277                      for ($x = 0; $x < ($src_x); $x++) {
1278                         $color = imagecolorat($src_img, $x, $y);
1279                         imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1280                      }
1281                   }
1282                   break;
1283
1284                case 180:
1285                   for ($y = 0; $y < ($src_y); $y++) {
1286                      for ($x = 0; $x < ($src_x); $x++) {
1287                         $color = imagecolorat($src_img, $x, $y);
1288                         imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1289                      }
1290                   }
1291                   break;
1292
1293                default:
1294                   $rotate = $src_img;
1295                   break;
1296             };
1297
1298             return $rotate;
1299
1300          }
1301
1302          $img = imagerotate($img, $degrees);
1303
1304       }
1305
1306       return $img;
1307
1308    } // rotateImage()
1309
1310    /**
1311     * return all assigned tags for the specified photo
1312     */
1313    private function get_photo_tags($idx)
1314    {
1315       $result = $this->db->db_query("
1316          SELECT t.id, t.name
1317          FROM tags t
1318          INNER JOIN photo_tags pt
1319             ON t.id=pt.tag_id
1320          WHERE pt.photo_id='". $idx ."'
1321       ");
1322
1323       $tags = Array();
1324
1325       while($row = $this->db->db_fetch_object($result))
1326          $tags[$row['id']] = $row['name'];
1327
1328       return $tags;
1329
1330    } // get_photo_tags()
1331
1332    /**
1333     * create on-the-fly images with text within
1334     */
1335    public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1336    {
1337       if (strlen($color) != 6) 
1338          $color = 000000;
1339
1340       $int = hexdec($color);
1341       $h = imagefontheight($font);
1342       $fw = imagefontwidth($font);
1343       $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1344       $lines = count($txt);
1345       $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1346       $bg = imagecolorallocate($im, 255, 255, 255);
1347       $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1348       $y = 0;
1349
1350       foreach ($txt as $text) {
1351          $x = (($w - ($fw * strlen($text))) / 2);
1352          imagestring($im, $font, $x, $y, $text, $color);
1353          $y += ($h + $space);
1354       }
1355
1356       Header("Content-type: image/png");
1357       ImagePng($im);
1358
1359    } // showTextImage()
1360
1361    /**
1362     * check if all requirements are met
1363     */
1364    private function checkRequirements()
1365    {
1366       if(!function_exists("imagecreatefromjpeg")) {
1367          print "PHP GD library extension is missing<br />\n";
1368          $missing = true;
1369       }
1370
1371       if(!function_exists("sqlite3_open")) {
1372          print "PHP SQLite3 library extension is missing<br />\n";
1373          $missing = true;
1374       }
1375
1376       /* Check for HTML_AJAX PEAR package, lent from Horde project */
1377       ini_set('track_errors', 1);
1378       @include_once 'HTML/AJAX/Server.php';
1379       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1380          print "PEAR HTML_AJAX package is missing<br />\n";
1381          $missing = true;
1382       }
1383       @include_once 'Calendar/Calendar.php';
1384       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1385          print "PEAR Calendar package is missing<br />\n";
1386          $missing = true;
1387       }
1388       ini_restore('track_errors');
1389
1390       if(isset($missing))
1391          return false;
1392
1393       return true;
1394
1395    } // checkRequirements()
1396
1397    private function _debug($text)
1398    {
1399       if($this->fromcmd) {
1400          print $text;
1401       }
1402
1403    } // _debug()
1404
1405    /**
1406     * check if specified MIME type is supported
1407     */
1408    public function checkifImageSupported($mime)
1409    {
1410       if(in_array($mime, Array("image/jpeg")))
1411          return true;
1412
1413       return false;
1414
1415    } // checkifImageSupported()
1416
1417    public function _error($text)
1418    {
1419       switch($this->cfg->logging) {
1420          case 'display':
1421             print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1422             print $text;
1423             break;
1424          case 'errorlog':  
1425             error_log($text);
1426             break;
1427          case 'logfile':
1428             error_log($text, 3, $his->cfg->log_file);
1429             break;
1430       }
1431
1432    } // _error()
1433
1434    /**
1435     * output calendard input fields
1436     */
1437    private function get_calendar($mode)
1438    {
1439       $year = $_SESSION[$mode .'_date'] ? date("Y", strtotime($_SESSION[$mode .'_date'])) : date("Y");
1440       $month = $_SESSION[$mode .'_date'] ? date("m", strtotime($_SESSION[$mode .'_date'])) : date("m");
1441       $day = $_SESSION[$mode .'_date'] ? date("d", strtotime($_SESSION[$mode .'_date'])) : date("d");
1442
1443       $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1444       if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1445       $output.= " />\n";
1446       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1447       if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1448       $output.= " />\n";
1449       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1450       if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1451       $output.= " />\n";
1452       return $output;
1453
1454    } // get_calendar()
1455
1456    /**
1457     * output calendar matrix
1458     */
1459    public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1460    {
1461       if (!isset($year)) $year = date('Y');
1462       if (!isset($month)) $month = date('m');
1463       if (!isset($day)) $day = date('d');
1464       $rows = 1;
1465       $cols = 1;
1466       $matrix = Array();
1467
1468       require_once CALENDAR_ROOT.'Month/Weekdays.php';
1469       require_once CALENDAR_ROOT.'Day.php';
1470
1471       // Build the month
1472       $month = new Calendar_Month_Weekdays($year,$month);
1473
1474       // Create links
1475       $prevStamp = $month->prevMonth(true);
1476       $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1477       $nextStamp = $month->nextMonth(true);
1478       $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1479
1480       $selectedDays = array (
1481          new Calendar_Day($year,$month,$day),
1482          new Calendar_Day($year,12,25),
1483       );
1484
1485       // Build the days in the month
1486       $month->build($selectedDays);
1487
1488       $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1489       $this->tmpl->assign('prev_month', $prev);
1490       $this->tmpl->assign('next_month', $next);
1491
1492       while ( $day = $month->fetch() ) {
1493    
1494          if(!isset($matrix[$rows]))
1495             $matrix[$rows] = Array();
1496
1497          $string = "";
1498
1499          $dayStamp = $day->thisDay(true);
1500          $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1501
1502          // isFirst() to find start of week
1503          if ( $day->isFirst() )
1504             $string.= "<tr>\n";
1505
1506          if ( $day->isSelected() ) {
1507             $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1508          } else if ( $day->isEmpty() ) {
1509             $string.= "<td>&nbsp;</td>\n";
1510          } else {
1511             $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1512          }
1513
1514          // isLast() to find end of week
1515          if ( $day->isLast() )
1516             $string.= "</tr>\n";
1517
1518          $matrix[$rows][$cols] = $string;
1519
1520          $cols++;
1521
1522          if($cols > 7) {
1523             $cols = 1;
1524             $rows++;
1525          }
1526       }
1527
1528       $this->tmpl->assign('matrix', $matrix);
1529       $this->tmpl->assign('rows', $rows);
1530       $this->tmpl->show("calendar.tpl");
1531
1532    } // get_calendar_matrix()
1533
1534    /**
1535     * output export page
1536     */
1537    public function getExport($mode)
1538    {
1539       $pictures = $this->getPhotoSelection();
1540       $current_tags = $this->getCurrentTags();  
1541
1542       if(!isset($_SERVER['HTTPS'])) $protocol = "http";
1543       else $protocol = "https";
1544
1545       $server_name = $_SERVER['SERVER_NAME'];
1546
1547       foreach($pictures as $picture) {
1548
1549          $orig_url = $protocol ."://". $server_name . $this->cfg->web_path ."index.php?mode=showp&id=". $picture;
1550          if($current_tags != "") {
1551             $orig_url.= "&tags=". $current_tags;
1552          } 
1553          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1554             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1555          }
1556
1557          $thumb_url = $protocol ."://". $server_name . $this->cfg->web_path ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1558
1559          switch($mode) {
1560
1561             case 'HTML':
1562                // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1563                print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1564                break;
1565                
1566             case 'MoinMoin':
1567                // "[%pictureurl% %thumbnailurl%]"
1568                print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1569                break;
1570
1571             case 'MoinMoinList':
1572                // " * [%pictureurl% %thumbnailurl%]"
1573                print "&nbsp;" . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1574                break;
1575          }
1576
1577       }
1578
1579    } // getExport()
1580
1581    /**
1582     * return all selected tags as one string
1583     */
1584    private function getCurrentTags()
1585    {
1586       $current_tags = "";
1587       if($_SESSION['selected_tags'] != "") {
1588          foreach($_SESSION['selected_tags'] as $tag)
1589             $current_tags.= $tag .",";
1590          $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1591       }
1592       return $current_tags;
1593
1594    } // getCurrentTags()
1595
1596    /**
1597     * return the current photo
1598     */
1599    public function getCurrentPhoto()
1600    {
1601       if(isset($_SESSION['current_photo'])) {
1602          print $_SESSION['current_photo'];
1603       }
1604    } // getCurrentPhoto()
1605
1606    /**
1607     * tells the client browser what to do
1608     *
1609     * this function is getting called via AJAX by the
1610     * client browsers. it will tell them what they have
1611     * to do next. This is necessary for directly jumping
1612     * into photo index or single photo view when the are
1613     * requested with specific URLs
1614     */
1615    public function whatToDo()
1616    {
1617       if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1618          return "show_photo";
1619       }
1620       elseif((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) ||
1621          (isset($_SESSION['from_date']) && isset($_SESSION['to_date']))) {
1622          return "showpi";
1623       }
1624       elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1625          return "showpi";
1626       }
1627
1628       return "nothing special";
1629
1630    } // whatToDo()
1631
1632    /**
1633     * return the current process-user
1634     */
1635    private function getuid()
1636    {
1637       if($uid = posix_getuid()) {
1638          if($user = posix_getpwuid($uid)) {
1639             return $user['name'];
1640          }
1641       }
1642    
1643       return 'n/a';
1644    
1645    } // getuid()
1646
1647    /**
1648     * returns a select-dropdown box to select photo index sort parameters
1649     */
1650    private function get_sort_field()
1651    {
1652       $output = "<select name=\"sort_order\">";
1653       foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1654          $output.= "<option value=\"". $sort_order ."\"";
1655          if($sort_order == $_SESSION['sort_order']) {
1656             $output.= " selected=\"selected\"";
1657          }
1658          $output.= ">". $sort_order ."</option>";
1659       }
1660       $output.= "</select>";
1661       return $output;
1662
1663    } // get_sort_field()
1664
1665    /**
1666     * returns the currently selected sort order
1667     */ 
1668    private function get_sort_order()
1669    {
1670       switch($_SESSION['sort_order']) {
1671          case 'date_asc':
1672             return " ORDER BY p.time ASC";
1673             break;
1674          case 'date_desc':
1675             return " ORDER BY p.time DESC";
1676             break;
1677          case 'name_asc':
1678             return " ORDER BY p.name ASC";
1679             break;
1680          case 'name_desc':
1681             return " ORDER BY p.name DESC";
1682             break;
1683       }
1684
1685    } // get_sort_order()
1686
1687    /***
1688      * return the next to be shown slide show image
1689      *
1690      * this function returns the URL of the next image
1691      * in the slideshow sequence.
1692      */
1693    public function getNextSlideShowImage()
1694    {
1695       $all_photos = $this->getPhotoSelection();
1696
1697       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1) 
1698          $_SESSION['slideshow_img'] = 0;
1699       else
1700          $_SESSION['slideshow_img']++;
1701
1702       $server_name = $_SERVER['SERVER_NAME'];
1703       if(!isset($_SERVER['HTTPS'])) $protocol = "http";
1704       else $protocol = "https";
1705
1706       return $protocol ."://". $server_name . $this->cfg->web_path ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1707
1708    } // getNextSlideShowImage()
1709
1710    /***
1711      * return the previous to be shown slide show image
1712      *
1713      * this function returns the URL of the previous image
1714      * in the slideshow sequence.
1715      */
1716    public function getPrevSlideShowImage()
1717    {
1718       $all_photos = $this->getPhotoSelection();
1719
1720       if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1721          $_SESSION['slideshow_img'] = 0;
1722       else
1723          $_SESSION['slideshow_img']--;
1724
1725       $server_name = $_SERVER['SERVER_NAME'];
1726       if(!isset($_SERVER['HTTPS'])) $protocol = "http";
1727       else $protocol = "https";
1728
1729       return $protocol ."://". $server_name . $this->cfg->web_path ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1730
1731    } // getPrevSlideShowImage()
1732
1733    public function resetSlideShow()
1734    {
1735       if(isset($_SESSION['slideshow_img']))
1736          unset($_SESSION['slideshow_img']);
1737    } // resetSlideShow()
1738    
1739    /***
1740      * get random photo
1741      *
1742      * this function will get all photos from the fspot
1743      * database and randomly return ONE entry
1744      *
1745      * saddly there is yet no sqlite3 function which returns
1746      * the bulk result in array, so we have to fill up our
1747      * own here.
1748      */ 
1749    public function get_random_photo()
1750    {
1751       $all = Array();
1752
1753       $result = $this->db->db_query("
1754          SELECT id
1755          FROM photos
1756       ");
1757       
1758       while($row = $this->db->db_fetch_object($result)) {
1759          array_push($all, $row['id']);
1760       }
1761
1762       return $all[array_rand($all)];
1763
1764    } // get_random_photo()
1765
1766 }
1767
1768 ?>