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