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