issue54, sort order can now be modified
[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.= "WHERE pt1.tag_id=". $_SESSION['selected_tags'][0];
640             for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
641                $query_str.= "
642                   AND pt". ($i+1) .".tag_id=". $_SESSION['selected_tags'][$i] ."
643                "; 
644             }
645             if(isset($additional_where_cond)) 
646                $query_str.= "AND ". $additional_where_cond;
647             if(isset($order_str))
648                $query_str.= $order_str;
649          }
650
651          $result = $this->db->db_query($query_str);
652          while($row = $this->db->db_fetch_object($result)) {
653             array_push($matched_photos, $row['photo_id']);
654          }
655          return $matched_photos;
656       }
657
658       /* return all available photos */
659       $query_str = "
660          SELECT DISTINCT photo_id
661             FROM photo_tags pt
662          INNER JOIN photos p
663             ON p.id=pt.photo_id
664       ";
665       if(isset($additional_where_cond)) 
666          $query_str.= "WHERE ". $additional_where_cond ." ";
667       if(isset($order_str))
668          $query_str.= $order_str;
669
670       $result = $this->db->db_query($query_str);
671       while($row = $this->db->db_fetch_object($result)) {
672          array_push($matched_photos, $row['photo_id']);
673       }
674       return $matched_photos;
675
676    } // getPhotoSelection()
677
678     /**
679     * control HTML ouput for photo index
680     *
681     * this function provides all the necessary information
682     * for the photo index template.
683     */
684    public function showPhotoIndex()
685    {
686       $photos = $this->getPhotoSelection();
687
688       $count = count($photos);
689
690       if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
691          $anchor = $_SESSION['begin_with'];
692
693       if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
694
695          $begin_with = 0;
696          $end_with = $count;
697
698       }
699       elseif($this->cfg->rows_per_page > 0) {
700
701          if(!$_SESSION['begin_with'] || $_SESSION['begin_with'] == 0)
702             $begin_with = 0;
703          else {
704
705             $begin_with = $_SESSION['begin_with'];
706
707             // verify $begin_with - perhaps the thumbs-per-rows or
708             // rows-per-page variables have changed or the jump back
709             // from a photo wasn't exact - so calculate the real new
710             // starting point
711             $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
712             for($i = 0; $i <= $count; $i+=$multiplicator) {
713                if($begin_with >= $i && $begin_with < $i+$multiplicator) {
714                   $begin_with = $i;
715                   break;
716                }
717             }
718          }
719
720          $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
721       }
722
723    
724       $rows = 0;
725       $cols = 0;
726       $images[$rows] = Array();
727       $img_height[$rows] = Array();
728       $img_width[$rows] = Array();
729       $img_id[$rows] = Array();
730       $img_name[$rows] = Array();
731       $img_title = Array();
732
733       for($i = $begin_with; $i < $end_with; $i++) {
734
735          $images[$rows][$cols] = $photos[$i];
736          $img_id[$rows][$cols] = $i;
737          $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
738          $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
739
740          $thumb_path = $this->cfg->base_path ."/thumbs/". $this->cfg->thumb_width ."_". $this->getMD5($photos[$i]);
741
742          if(file_exists($thumb_path)) {
743             $info = getimagesize($thumb_path); 
744             $img_width[$rows][$cols] = $info[0];
745             $img_height[$rows][$cols] = $info[1];
746          }
747
748          if($cols == $this->cfg->thumbs_per_row-1) {
749             $cols = 0;
750             $rows++;
751             $images[$rows] = Array();
752             $img_width[$rows] = Array();
753             $img_height[$rows] = Array();
754          }
755          else {
756             $cols++;
757          }
758       } 
759
760       // +1 for for smarty's selection iteration
761       $rows++;
762
763       if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
764          $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
765
766       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
767          $this->tmpl->assign('from_date', $_SESSION['from_date']);
768          $this->tmpl->assign('to_date', $_SESSION['to_date']);
769       }
770
771       if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
772          $this->tmpl->assign('tag_result', 1);
773       }
774
775       /* do we have to display the page selector ? */
776       if($this->cfg->rows_per_page != 0) {
777       
778          /* calculate the page switchers */
779          $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
780          $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
781
782          if($begin_with != 0) 
783             $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");"); 
784          if($end_with < $count)
785             $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");"); 
786
787          $photo_per_page  = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
788          $last_page = ceil($count / $photo_per_page);
789
790          /* get the current selected page */
791          if($begin_with == 0) {
792             $current_page = 1;
793          } else {
794             $current_page = 0;
795             for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
796                $current_page++;
797             }
798          } 
799
800          $dotdot_made = 0;
801
802          for($i = 1; $i <= $last_page; $i++) {
803
804             if($current_page == $i)
805                $style = "style=\"font-size: 125%;\"";
806             elseif($current_page-1 == $i || $current_page+1 == $i)
807                $style = "style=\"font-size: 105%;\"";
808             elseif(($current_page-5 >= $i) && ($i != 1) ||
809                ($current_page+5 <= $i) && ($i != $last_page))
810                $style = "style=\"font-size: 75%;\"";
811             else
812                $style = "";
813
814             $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
815                if($style != "")
816                   $select.= $style;
817             $select.= ">". $i ."</a>&nbsp;";
818
819             // until 9 pages we show the selector from 1-9
820             if($last_page <= 9) {
821                $page_select.= $select;
822                continue;
823             } else {
824                if($i == 1 /* first page */ || 
825                   $i == $last_page /* last page */ ||
826                   $i == $current_page /* current page */ ||
827                   $i == ceil($last_page * 0.25) /* first quater */ ||
828                   $i == ceil($last_page * 0.5) /* half */ ||
829                   $i == ceil($last_page * 0.75) /* third quater */ ||
830                   (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
831                   (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 */ ||
832                   $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
833                   $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
834
835                   $page_select.= $select;
836                   $dotdot_made = 0;
837                   continue;
838
839                }
840             }
841
842             if(!$dotdot_made) {
843                $page_select.= ".........&nbsp;";
844                $dotdot_made = 1;
845             }
846          }
847
848          /* only show the page selector if we have more then one page */
849          if($last_page > 1)
850             $this->tmpl->assign('page_selector', $page_select);
851       }
852
853       
854       $current_tags = $this->getCurrentTags();
855       $extern_link = "index.php?mode=showpi";
856       if($current_tags != "") {
857          $extern_link.= "&tags=". $current_tags;
858       }
859       if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
860          $extern_link.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
861       }
862
863       $export_link = "index.php?mode=export";
864
865       $this->tmpl->assign('extern_link', $extern_link);
866       $this->tmpl->assign('export_link', $export_link);
867       $this->tmpl->assign('count', $count);
868       $this->tmpl->assign('width', $this->cfg->thumb_width);
869       $this->tmpl->assign('images', $images);
870       $this->tmpl->assign('img_width', $img_width);
871       $this->tmpl->assign('img_height', $img_height);
872       $this->tmpl->assign('img_id', $img_id);
873       $this->tmpl->assign('img_name', $img_name);
874       $this->tmpl->assign('img_title', $img_title);
875       $this->tmpl->assign('rows', $rows);
876       $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
877
878       $this->tmpl->show("photo_index.tpl");
879
880       if(isset($anchor))
881          print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
882
883    } // showPhotoIndex()
884
885    /**
886     * show credit template
887     */
888    public function showCredits()
889    {
890       $this->tmpl->assign('version', $this->cfg->version);
891       $this->tmpl->assign('product', $this->cfg->product);
892       $this->tmpl->show("credits.tpl");
893
894    } // showCredits()
895
896    /**
897     * create_thumbnails for the requested width
898     *
899     * this function creates image thumbnails of $orig_image
900     * stored as $thumb_image. It will check if the image is
901     * in a supported format, if necessary rotate the image
902     * (based on EXIF orientation meta headers) and re-sizing.
903     */
904    public function create_thumbnail($orig_image, $thumb_image, $width)
905    {  
906       if(!file_exists($orig_image)) {
907          return false;
908       }
909
910       $details = getimagesize($orig_image);
911       
912       /* check if original photo is a support image type */
913       if(!$this->checkifImageSupported($details['mime']))
914          return false;
915
916       $meta = $this->get_meta_informations($orig_image);
917
918       $rotate = 0;
919       $flip = false;
920
921       switch($meta['Orientation']) {
922
923          case 1: /* top, left */
924             $rotate = 0; $flip = false; break;
925          case 2: /* top, right */
926             $rotate = 0; $flip = true; break;
927          case 3: /* bottom, left */
928             $rotate = 180; $flip = false; break;
929          case 4: /* bottom, right */
930             $rotate = 180; $flip = true; break;
931          case 5: /* left side, top */
932             $rotate = 90; $flip = true; break;
933          case 6: /* right side, top */
934             $rotate = 90; $flip = false; break;
935          case 7: /* left side, bottom */
936             $rotate = 270; $flip = true; break;
937          case 8: /* right side, bottom */
938             $rotate = 270; $flip = false; break;
939       }
940
941       $src_img = @imagecreatefromjpeg($orig_image);
942
943       if(!$src_img) {
944          print "Can't load image from ". $orig_image ."\n";
945          return false;
946       }
947
948       /* grabs the height and width */
949       $cur_width = imagesx($src_img);
950       $cur_height = imagesy($src_img);
951
952       // If requested width is more then the actual image width,
953       // do not generate a thumbnail, instead safe the original
954       // as thumbnail but with lower quality
955
956       if($width >= $cur_width) {
957          $result = imagejpeg($src_img, $thumb_image, 75);
958          imagedestroy($src_img);
959          return true;
960       }
961
962       // If the image will be rotate because EXIF orientation said so
963       // 'virtually rotate' the image for further calculations
964       if($rotate == 90 || $rotate == 270) {
965          $tmp = $cur_width;
966          $cur_width = $cur_height;
967          $cur_height = $tmp;
968       }
969
970       /* calculates aspect ratio */
971       $aspect_ratio = $cur_height / $cur_width;
972
973       /* sets new size */
974       if($aspect_ratio < 1) {
975          $new_w = $width;
976          $new_h = abs($new_w * $aspect_ratio);
977       } else {
978          /* 'virtually' rotate the image and calculate it's ratio */
979          $tmp_w = $cur_height;
980          $tmp_h = $cur_width;
981          /* now get the ratio from the 'rotated' image */
982          $tmp_ratio = $tmp_h/$tmp_w;
983          /* now calculate the new dimensions */
984          $tmp_w = $width;
985          $tmp_h = abs($tmp_w * $tmp_ratio);
986
987          // now that we know, how high they photo should be, if it
988          // gets rotated, use this high to scale the image
989          $new_h = $tmp_h;
990          $new_w = abs($new_h / $aspect_ratio);
991
992          // If the image will be rotate because EXIF orientation said so
993          // now 'virtually rotate' back the image for the image manipulation
994          if($rotate == 90 || $rotate == 270) {
995             $tmp = $new_w;
996             $new_w = $new_h;
997             $new_h = $tmp;
998          }
999       }
1000
1001       /* creates new image of that size */
1002       $dst_img = imagecreatetruecolor($new_w, $new_h);
1003
1004       imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1005
1006       /* copies resized portion of original image into new image */
1007       imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1008
1009       /* needs the image to be flipped horizontal? */
1010       if($flip) {
1011          print "(FLIP)";
1012          $image = $dst_img;
1013          for($x = 0; $x < $new_w; $x++) {
1014             imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1015          }
1016       }
1017
1018       if($rotate) {
1019          $this->_debug("(ROTATE)");
1020          $dst_img = $this->rotateImage($dst_img, $rotate);
1021       }
1022
1023       /* write down new generated file */
1024       $result = imagejpeg($dst_img, $thumb_image, 75);
1025
1026       /* free your mind */
1027       imagedestroy($dst_img);
1028       imagedestroy($src_img);
1029
1030       if($result === false) {
1031          print "Can't write thumbnail ". $thumb_image ."\n";
1032          return false;
1033       }
1034
1035       return true;
1036
1037    } // create_thumbnail()
1038
1039    /**
1040     * return all exif meta data from the file
1041     */
1042    public function get_meta_informations($file)
1043    {
1044       return exif_read_data($file);
1045
1046    } // get_meta_informations()
1047
1048    /**
1049     * create phpfspot own sqlite database
1050     *
1051     * this function creates phpfspots own sqlite database
1052     * if it does not exist yet. this own is used to store
1053     * some necessary informations (md5 sum's, ...).
1054     */
1055    public function check_config_table()
1056    {
1057       // if the config table doesn't exist yet, create it
1058       if(!$this->cfg_db->db_check_table_exists("images")) {
1059          $this->cfg_db->db_exec("
1060             CREATE TABLE images (
1061                img_idx int primary key,
1062                img_md5 varchar(32)
1063             )
1064             ");
1065       }
1066
1067    } // check_config_table
1068
1069    /**
1070     * Generates a thumbnail from photo idx
1071     *
1072     * This function will generate JPEG thumbnails from provided F-Spot photo
1073     * indizes.
1074     *
1075     * 1. Check if all thumbnail generations (width) are already in place and
1076     *    readable
1077     * 2. Check if the md5sum of the original file has changed
1078     * 3. Generate the thumbnails if needed
1079     */
1080    public function gen_thumb($idx = 0, $force = 0)
1081    {
1082       $error = 0;
1083
1084       $resolutions = Array(
1085          $this->cfg->thumb_width,
1086          $this->cfg->photo_width,
1087          $this->cfg->mini_width,
1088       );
1089
1090       /* get details from F-Spot's database */
1091       $details = $this->get_photo_details($idx);
1092
1093       /* calculate file MD5 sum */
1094       $full_path = $this->translate_path($details['directory_path'])  ."/". $details['name'];
1095
1096       if(!file_exists($full_path)) {
1097          $this->_warning("File ". $full_path ." does not exist\n");
1098          return;
1099       }
1100
1101       if(!is_readable($full_path)) {
1102          $this->_warning("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1103          return;
1104       }
1105
1106       $file_md5 = md5_file($full_path);
1107
1108       $this->_debug("Image [". $idx ."] ". $details['name'] ." Thumbnails:");
1109
1110       foreach($resolutions as $resolution) {
1111
1112          $thumb_path = $this->cfg->base_path ."/thumbs/". $resolution ."_". $file_md5;
1113
1114          /* if the thumbnail file doesn't exist, create it */
1115          if(!file_exists($thumb_path)) {
1116
1117             $this->_debug(" ". $resolution ."px");
1118             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1119                $error = 1;
1120          }
1121          /* if the file hasn't changed there is no need to regen the thumb */
1122          elseif($file_md5 != $this->getMD5($idx) || $force) {
1123
1124             $this->_debug(" ". $resolution ."px");
1125             if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1126                $error = 1;
1127
1128          }
1129       }
1130
1131       /* set the new/changed MD5 sum for the current photo */
1132       if(!$error) {
1133          $this->setMD5($idx, $file_md5);
1134       }
1135
1136       $this->_debug("\n");
1137
1138    } // gen_thumb()
1139
1140    /**
1141     * returns stored md5 sum for a specific photo
1142     *
1143     * this function queries the phpfspot database for a
1144     * stored MD5 checksum of the specified photo
1145     */
1146    public function getMD5($idx)
1147    {
1148       $result = $this->cfg_db->db_query("
1149          SELECT img_md5 
1150          FROM images
1151          WHERE img_idx='". $idx ."'
1152       ");
1153
1154       if(!$result)
1155          return 0;
1156
1157       $img = $this->cfg_db->db_fetch_object($result);
1158       return $img['img_md5'];
1159       
1160    } // getMD5()
1161
1162    /**
1163     * set MD5 sum for the specific photo
1164     */
1165    private function setMD5($idx, $md5)
1166    {
1167       $result = $this->cfg_db->db_exec("
1168          REPLACE INTO images (img_idx, img_md5)
1169          VALUES ('". $idx ."', '". $md5 ."')
1170       ");
1171
1172    } // setMD5()
1173
1174    /**
1175     * store current tag condition
1176     *
1177     * this function stores the current tag condition
1178     * (AND or OR) in the users session variables
1179     */
1180    public function setTagCondition($mode)
1181    {
1182       $_SESSION['tag_condition'] = $mode;
1183
1184    } // setTagCondition()
1185
1186    /** 
1187     * invoke tag & date search 
1188     *
1189     * this function will return all matching tags and store
1190     * them in the session variable selected_tags. furthermore
1191     * it also handles the date search.
1192     * getPhotoSelection() will then only return the matching
1193     * photos.
1194     */
1195    public function startSearch($searchfor, $from, $to, $sort_order)
1196    {
1197       $_SESSION['searchfor'] = $searchfor;
1198       $_SESSION['from_date'] = $from;
1199       $_SESSION['to_date'] = $to;
1200       $_SESSION['sort_order'] = $sort_order;
1201
1202       if($searchfor != "") {
1203          /* new search, reset the current selected tags */
1204          $_SESSION['selected_tags'] = Array();
1205          foreach($this->avail_tags as $tag) {
1206             if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1207                array_push($_SESSION['selected_tags'], $tag);
1208          }
1209       }
1210    } // startSearch()
1211
1212    /**
1213     * rotate image
1214     *
1215     * this function rotates the image according the
1216     * specified angel.
1217     */
1218    private function rotateImage($img, $degrees)
1219    {
1220       if(function_exists("imagerotate")) {
1221          $img = imagerotate($img, $degrees, 0);
1222       } else {
1223          function imagerotate($src_img, $angle)
1224          {
1225             $src_x = imagesx($src_img);
1226             $src_y = imagesy($src_img);
1227             if ($angle == 180) {
1228                $dest_x = $src_x;
1229                $dest_y = $src_y;
1230             }
1231             elseif ($src_x <= $src_y) {
1232                $dest_x = $src_y;
1233                $dest_y = $src_x;
1234             }
1235             elseif ($src_x >= $src_y) {
1236                $dest_x = $src_y;
1237                $dest_y = $src_x;
1238             }
1239                
1240             $rotate=imagecreatetruecolor($dest_x,$dest_y);
1241             imagealphablending($rotate, false);
1242                
1243             switch ($angle) {
1244             
1245                case 90:
1246                   for ($y = 0; $y < ($src_y); $y++) {
1247                      for ($x = 0; $x < ($src_x); $x++) {
1248                         $color = imagecolorat($src_img, $x, $y);
1249                         imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1250                      }
1251                   }
1252                   break;
1253
1254                case 270:
1255                   for ($y = 0; $y < ($src_y); $y++) {
1256                      for ($x = 0; $x < ($src_x); $x++) {
1257                         $color = imagecolorat($src_img, $x, $y);
1258                         imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1259                      }
1260                   }
1261                   break;
1262
1263                case 180:
1264                   for ($y = 0; $y < ($src_y); $y++) {
1265                      for ($x = 0; $x < ($src_x); $x++) {
1266                         $color = imagecolorat($src_img, $x, $y);
1267                         imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1268                      }
1269                   }
1270                   break;
1271
1272                default:
1273                   $rotate = $src_img;
1274                   break;
1275             };
1276
1277             return $rotate;
1278
1279          }
1280
1281          $img = imagerotate($img, $degrees);
1282
1283       }
1284
1285       return $img;
1286
1287    } // rotateImage()
1288
1289    /**
1290     * return all assigned tags for the specified photo
1291     */
1292    private function get_photo_tags($idx)
1293    {
1294       $result = $this->db->db_query("
1295          SELECT t.id, t.name
1296          FROM tags t
1297          INNER JOIN photo_tags pt
1298             ON t.id=pt.tag_id
1299          WHERE pt.photo_id='". $idx ."'
1300       ");
1301
1302       $tags = Array();
1303
1304       while($row = $this->db->db_fetch_object($result))
1305          $tags[$row['id']] = $row['name'];
1306
1307       return $tags;
1308
1309    } // get_photo_tags()
1310
1311    /**
1312     * create on-the-fly images with text within
1313     */
1314    public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1315    {
1316       if (strlen($color) != 6) 
1317          $color = 000000;
1318
1319       $int = hexdec($color);
1320       $h = imagefontheight($font);
1321       $fw = imagefontwidth($font);
1322       $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1323       $lines = count($txt);
1324       $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1325       $bg = imagecolorallocate($im, 255, 255, 255);
1326       $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1327       $y = 0;
1328
1329       foreach ($txt as $text) {
1330          $x = (($w - ($fw * strlen($text))) / 2);
1331          imagestring($im, $font, $x, $y, $text, $color);
1332          $y += ($h + $space);
1333       }
1334
1335       Header("Content-type: image/png");
1336       ImagePng($im);
1337
1338    } // showTextImage()
1339
1340    /**
1341     * check if all requirements are met
1342     */
1343    private function checkRequirements()
1344    {
1345       if(!function_exists("imagecreatefromjpeg")) {
1346          print "PHP GD library extension is missing<br />\n";
1347          $missing = true;
1348       }
1349
1350       if(!function_exists("sqlite3_open")) {
1351          print "PHP SQLite3 library extension is missing<br />\n";
1352          $missing = true;
1353       }
1354
1355       /* Check for HTML_AJAX PEAR package, lent from Horde project */
1356       ini_set('track_errors', 1);
1357       @include_once 'HTML/AJAX/Server.php';
1358       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1359          print "PEAR HTML_AJAX package is missing<br />\n";
1360          $missing = true;
1361       }
1362       @include_once 'Calendar/Calendar.php';
1363       if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1364          print "PEAR Calendar package is missing<br />\n";
1365          $missing = true;
1366       }
1367       ini_restore('track_errors');
1368
1369       if(isset($missing))
1370          return false;
1371
1372       return true;
1373
1374    } // checkRequirements()
1375
1376    private function _debug($text)
1377    {
1378       if($this->fromcmd) {
1379          print $text;
1380       }
1381
1382    } // _debug()
1383
1384    /**
1385     * check if specified MIME type is supported
1386     */
1387    public function checkifImageSupported($mime)
1388    {
1389       if(in_array($mime, Array("image/jpeg")))
1390          return true;
1391
1392       return false;
1393
1394    } // checkifImageSupported()
1395
1396    public function _warning($text)
1397    {
1398       print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1399       print $text;
1400
1401    } // _warning()
1402
1403    /**
1404     * output calendard input fields
1405     */
1406    private function get_calendar($mode)
1407    {
1408       $year = $_SESSION[$mode .'_date'] ? date("Y", strtotime($_SESSION[$mode .'_date'])) : date("Y");
1409       $month = $_SESSION[$mode .'_date'] ? date("m", strtotime($_SESSION[$mode .'_date'])) : date("m");
1410       $day = $_SESSION[$mode .'_date'] ? date("d", strtotime($_SESSION[$mode .'_date'])) : date("d");
1411
1412       $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1413       if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1414       $output.= " />\n";
1415       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1416       if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1417       $output.= " />\n";
1418       $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1419       if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1420       $output.= " />\n";
1421       return $output;
1422
1423    } // get_calendar()
1424
1425    /**
1426     * output calendar matrix
1427     */
1428    public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1429    {
1430       if (!isset($year)) $year = date('Y');
1431       if (!isset($month)) $month = date('m');
1432       if (!isset($day)) $day = date('d');
1433       $rows = 1;
1434       $cols = 1;
1435       $matrix = Array();
1436
1437       require_once CALENDAR_ROOT.'Month/Weekdays.php';
1438       require_once CALENDAR_ROOT.'Day.php';
1439
1440       // Build the month
1441       $month = new Calendar_Month_Weekdays($year,$month);
1442
1443       // Create links
1444       $prevStamp = $month->prevMonth(true);
1445       $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1446       $nextStamp = $month->nextMonth(true);
1447       $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1448
1449       $selectedDays = array (
1450          new Calendar_Day($year,$month,$day),
1451          new Calendar_Day($year,12,25),
1452       );
1453
1454       // Build the days in the month
1455       $month->build($selectedDays);
1456
1457       $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1458       $this->tmpl->assign('prev_month', $prev);
1459       $this->tmpl->assign('next_month', $next);
1460
1461       while ( $day = $month->fetch() ) {
1462    
1463          if(!isset($matrix[$rows]))
1464             $matrix[$rows] = Array();
1465
1466          $string = "";
1467
1468          $dayStamp = $day->thisDay(true);
1469          $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1470
1471          // isFirst() to find start of week
1472          if ( $day->isFirst() )
1473             $string.= "<tr>\n";
1474
1475          if ( $day->isSelected() ) {
1476             $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1477          } else if ( $day->isEmpty() ) {
1478             $string.= "<td>&nbsp;</td>\n";
1479          } else {
1480             $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1481          }
1482
1483          // isLast() to find end of week
1484          if ( $day->isLast() )
1485             $string.= "</tr>\n";
1486
1487          $matrix[$rows][$cols] = $string;
1488
1489          $cols++;
1490
1491          if($cols > 7) {
1492             $cols = 1;
1493             $rows++;
1494          }
1495       }
1496
1497       $this->tmpl->assign('matrix', $matrix);
1498       $this->tmpl->assign('rows', $rows);
1499       $this->tmpl->show("calendar.tpl");
1500
1501    } // get_calendar_matrix()
1502
1503    /**
1504     * output export page
1505     */
1506    public function getExport($mode)
1507    {
1508       $pictures = $this->getPhotoSelection();
1509       $current_tags = $this->getCurrentTags();  
1510
1511       if(!isset($_SERVER['HTTPS'])) $protocol = "http";
1512       else $protocol = "https";
1513
1514       $server_name = $_SERVER['SERVER_NAME'];
1515
1516       foreach($pictures as $picture) {
1517
1518          $orig_url = $protocol ."://". $server_name . $this->cfg->web_path ."index.php?mode=showp&id=". $picture;
1519          if($current_tags != "") {
1520             $orig_url.= "&tags=". $current_tags;
1521          } 
1522          if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1523             $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1524          }
1525
1526          $thumb_url = $protocol ."://". $server_name . $this->cfg->web_path ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1527
1528          switch($mode) {
1529
1530             case 'HTML':
1531                // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1532                print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1533                break;
1534                
1535             case 'MoinMoin':
1536                // [%pictureurl% %thumbnailurl%]
1537                print htmlspecialchars(" * [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1538                break;
1539          }
1540
1541       }
1542
1543    } // getExport()
1544
1545    /**
1546     * return all selected tags as one string
1547     */
1548    private function getCurrentTags()
1549    {
1550       $current_tags = "";
1551       if($_SESSION['selected_tags'] != "") {
1552          foreach($_SESSION['selected_tags'] as $tag)
1553             $current_tags.= $tag .",";
1554          $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1555       }
1556       return $current_tags;
1557
1558    } // getCurrentTags()
1559
1560    /**
1561     * return the current photo
1562     */
1563    public function getCurrentPhoto()
1564    {
1565       if(isset($_SESSION['current_photo'])) {
1566          print $_SESSION['current_photo'];
1567       }
1568    } // getCurrentPhoto()
1569
1570    /**
1571     * tells the client browser what to do
1572     *
1573     * this function is getting called via AJAX by the
1574     * client browsers. it will tell them what they have
1575     * to do next. This is necessary for directly jumping
1576     * into photo index or single photo view when the are
1577     * requested with specific URLs
1578     */
1579    public function whatToDo()
1580    {
1581       if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1582          return "show_photo";
1583       }
1584       elseif((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) ||
1585          (isset($_SESSION['from_date']) && isset($_SESSION['to_date']))) {
1586          return "showpi";
1587       }
1588       elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1589          return "showpi";
1590       }
1591
1592       return "nothing special";
1593
1594    } // whatToDo()
1595
1596    /**
1597     * return the current process-user
1598     */
1599    private function getuid()
1600    {
1601       if($uid = posix_getuid()) {
1602          if($user = posix_getpwuid($uid)) {
1603             return $user['name'];
1604          }
1605       }
1606    
1607       return 'n/a';
1608    
1609    } // getuid()
1610
1611    /**
1612     * returns a select-dropdown box to select photo index sort parameters
1613     */
1614    private function get_sort_field()
1615    {
1616       $output = "<select name=\"sort_order\">";
1617       foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1618          $output.= "<option value=\"". $sort_order ."\"";
1619          if($sort_order == $_SESSION['sort_order']) {
1620             $output.= " selected=\"selected\"";
1621          }
1622          $output.= ">". $sort_order ."</option>";
1623       }
1624       $output.= "</select>";
1625       return $output;
1626
1627    } // get_sort_field()
1628
1629    /**
1630     * returns the currently selected sort order
1631     */ 
1632    private function get_sort_order()
1633    {
1634       switch($_SESSION['sort_order']) {
1635          case 'date_asc':
1636             return "ORDER BY p.time ASC";
1637             break;
1638          case 'date_desc':
1639             return "ORDER BY p.time DESC";
1640             break;
1641          case 'name_asc':
1642             return "ORDER BY p.name ASC";
1643             break;
1644          case 'name_desc':
1645             return "ORDER BY p.name DESC";
1646             break;
1647       }
1648
1649       return $_SESSION['sort_order'];
1650
1651    } // get_sort_order()
1652 }
1653
1654 ?>