3 /***************************************************************************
5 * Copyright (c) by Andreas Unterkircher, unki@netshadow.at
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
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.
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.
22 ***************************************************************************/
24 require_once "phpfspot_cfg.php";
25 require_once "phpfspot_db.php";
40 * this function will be called on class construct
41 * and will check requirements, loads configuration,
42 * open databases and start the user session
44 public function __construct()
46 $this->cfg = new PHPFSPOT_CFG;
48 /* set application name and version information */
49 $this->cfg->product = "phpfspot";
50 $this->cfg->version = "1.2";
52 /* Check necessary requirements */
53 if(!$this->checkRequirements()) {
57 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
58 if(!is_writeable($this->cfg->fspot_db)) {
59 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
63 $this->dbver = $this->getFspotDBVersion();
65 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
66 print dirname($this->cfg->phpfspot_db) .": directory is not writeable!";
70 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
71 if(!is_writeable($this->cfg->phpfspot_db)) {
72 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
75 $this->check_config_table();
77 /* include Smarty template engine */
78 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
81 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
82 /* overload Smarty class if our own template handler */
83 require_once "phpfspot_tmpl.php";
84 $this->tmpl = new PHPFSPOT_TMPL($this);
86 /* check if all necessary indices exist */
87 $this->checkDbIndices();
91 if(!isset($_SESSION['tag_condition']))
92 $_SESSION['tag_condition'] = 'or';
94 if(!isset($_SESSION['sort_order']))
95 $_SESSION['sort_order'] = 'date_asc';
97 if(!isset($_SESSION['searchfor']))
98 $_SESSION['searchfor'] = '';
100 // if begin_with is still set but rows_per_page is now 0, unset it
101 if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
102 unset($_SESSION['begin_with']);
106 public function __destruct()
112 * show - generate html output
114 * this function can be called after the constructor has
115 * prepared everyhing. it will load the index.tpl smarty
116 * template. if necessary it will registere pre-selects
117 * (photo index, photo, tag search, date search) into
120 public function show()
122 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
123 $this->tmpl->assign('page_title', $this->cfg->page_title);
124 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
125 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
127 $_SESSION['start_action'] = $_GET['mode'];
129 switch($_GET['mode']) {
131 if(isset($_GET['tags'])) {
132 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
134 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
135 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
137 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
138 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
142 if(isset($_GET['tags'])) {
143 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
144 $_SESSION['start_action'] = 'showp';
146 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
147 $_SESSION['current_photo'] = $_GET['id'];
148 $_SESSION['start_action'] = 'showp';
150 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
151 $_SESSION['from_date'] = strtotime($_GET['from_date']);
153 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
154 $_SESSION['to_date'] = strtotime($_GET['to_date']);
158 $this->tmpl->show("export.tpl");
162 $this->tmpl->show("slideshow.tpl");
166 if(isset($_GET['tags'])) {
167 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
169 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
170 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
172 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
173 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
180 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
181 $this->tmpl->assign('date_search_enabled', true);
183 $this->tmpl->assign('from_date', $this->get_calendar('from'));
184 $this->tmpl->assign('to_date', $this->get_calendar('to'));
185 $this->tmpl->assign('sort_field', $this->get_sort_field());
186 $this->tmpl->assign('content_page', 'welcome.tpl');
187 $this->tmpl->show("index.tpl");
192 * get_tags - grab all tags of f-spot's database
194 * this function will get all available tags from
195 * the f-spot database and store them within two
196 * arrays within this class for later usage. in
197 * fact, if the user requests (hide_tags) it will
198 * opt-out some of them.
200 * this function is getting called once by show()
202 private function get_tags()
204 $this->avail_tags = Array();
207 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
210 DISTINCT t1.id as id, t1.name as name
213 INNER JOIN photo_tags
214 pt2 ON pt1.photo_id=pt2.photo_id
220 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
222 t1.sort_priority ASC";
224 $result = $this->db->db_query($query_str);
228 $result = $this->db->db_query("
231 ORDER BY sort_priority ASC
235 while($row = $this->db->db_fetch_object($result)) {
237 $tag_id = $row['id'];
238 $tag_name = $row['name'];
240 /* if the user has specified to ignore this tag in phpfspot's
241 configuration, ignore it here so it does not get added to
244 if(in_array($row['name'], $this->cfg->hide_tags))
247 /* if you include the following if-clause and the user has specified
248 to only show certain tags which are specified in phpfspot's
249 configuration, ignore all others so they will not be added to the
251 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
252 !in_array($row['name'], $this->cfg->show_tags))
256 $this->tags[$tag_id] = $tag_name;
257 $this->avail_tags[$count] = $tag_id;
265 * extract all photo details
267 * retrieve all available details from f-spot's
268 * database and return them as object
270 public function get_photo_details($idx)
272 if($this->dbver < 9) {
274 SELECT p.id, p.name, p.time, p.directory_path, p.description
280 SELECT p.id, p.uri, p.time, p.description
285 /* if show_tags is set, only return details for photos which
286 are specified to be shown
288 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
290 INNER JOIN photo_tags pt
294 WHERE p.id='". $idx ."'
295 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
299 WHERE p.id='". $idx ."'
303 $result = $this->db->db_query($query_str);
305 if($this->dbver < 9) {
306 $result['uri'] = "file://". $result['directory_path'] ."/". $result['name'];
309 return $this->db->db_fetch_object($result);
311 } // get_photo_details
314 * returns aligned photo names
316 * this function returns aligned (length) names for
317 * an specific photo. If the length of the name exceeds
318 * $limit the name will be shrinked (...)
320 public function getPhotoName($idx, $limit = 0)
322 if($details = $this->get_photo_details($idx)) {
323 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
324 $name = $this->shrink_text($long_name, $limit);
334 * shrink text according provided limit
336 * If the length of the name exceeds $limit the
337 * text will be shortend and some content in between
338 * will be replaced with "..."
340 private function shrink_text($text, $limit)
342 if($limit != 0 && strlen($text) > $limit) {
343 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
351 * translate f-spoth photo path
353 * as the full-qualified path recorded in the f-spot database
354 * is usally not the same as on the webserver, this function
355 * will replace the path with that one specified in the cfg
357 public function translate_path($path, $width = 0)
359 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
364 * control HTML ouput for a single photo
366 * this function provides all the necessary information
367 * for the single photo template.
369 public function showPhoto($photo)
371 /* get all photos from the current photo selection */
372 $all_photos = $this->getPhotoSelection();
373 $count = count($all_photos);
375 for($i = 0; $i < $count; $i++) {
377 // $get_next will be set, when the photo which has to
378 // be displayed has been found - this means that the
379 // next available is in fact the NEXT image (for the
381 if(isset($get_next)) {
382 $next_img = $all_photos[$i];
386 /* the next photo is our NEXT photo */
387 if($all_photos[$i] == $photo) {
391 $previous_img = $all_photos[$i];
394 if($photo == $all_photos[$i]) {
399 $details = $this->get_photo_details($photo);
406 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
407 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
409 if(!file_exists($orig_path)) {
410 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
413 if(!is_readable($orig_path)) {
414 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
417 /* If the thumbnail doesn't exist yet, try to create it */
418 if(!file_exists($thumb_path)) {
419 $this->gen_thumb($photo, true);
420 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
423 /* get f-spot database meta information */
424 $meta = $this->get_meta_informations($orig_path);
426 /* If EXIF data are available, use them */
427 if(isset($meta['ExifImageWidth'])) {
428 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
430 $info = getimagesize($orig_path);
431 $meta_res = $info[0] ."x". $info[1];
434 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
435 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
436 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
438 $extern_link = "index.php?mode=showp&id=". $photo;
439 $current_tags = $this->getCurrentTags();
440 if($current_tags != "") {
441 $extern_link.= "&tags=". $current_tags;
443 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
444 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
447 $this->tmpl->assign('extern_link', $extern_link);
449 if(file_exists($thumb_path)) {
451 $info = getimagesize($thumb_path);
453 $this->tmpl->assign('description', $details['description']);
454 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
456 $this->tmpl->assign('width', $info[0]);
457 $this->tmpl->assign('height', $info[1]);
458 $this->tmpl->assign('ExifMadeOn', $meta_date);
459 $this->tmpl->assign('ExifMadeWith', $meta_make);
460 $this->tmpl->assign('ExifOrigResolution', $meta_res);
461 $this->tmpl->assign('ExifFileSize', $meta_size);
463 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
464 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
466 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
467 $this->tmpl->assign('current', $current);
470 $this->_error("Can't open file ". $thumb_path ."\n");
475 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
476 $this->tmpl->assign('prev_img', $previous_img);
480 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
481 $this->tmpl->assign('next_img', $next_img);
483 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
484 $this->tmpl->assign('photo_number', $i);
485 $this->tmpl->assign('photo_count', count($all_photos));
487 $this->tmpl->show("single_photo.tpl");
492 * all available tags and tag cloud
494 * this function outputs all available tags (time ordered)
495 * and in addition output them as tag cloud (tags which have
496 * many photos will appears more then others)
498 public function getAvailableTags()
504 $result = $this->db->db_query("
505 SELECT tag_id as id, count(tag_id) as quantity
515 while($row = $this->db->db_fetch_object($result)) {
516 $tags[$row['id']] = $row['quantity'];
519 // change these font sizes if you will
520 $max_size = 125; // max font size in %
521 $min_size = 75; // min font size in %
523 // get the largest and smallest array values
524 $max_qty = max(array_values($tags));
525 $min_qty = min(array_values($tags));
527 // find the range of values
528 $spread = $max_qty - $min_qty;
529 if (0 == $spread) { // we don't want to divide by zero
533 // determine the font-size increment
534 // this is the increase per tag quantity (times used)
535 $step = ($max_size - $min_size)/($spread);
537 // loop through our tag array
538 foreach ($tags as $key => $value) {
540 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
543 // calculate CSS font-size
544 // find the $value in excess of $min_qty
545 // multiply by the font-size increment ($size)
546 // and add the $min_size set above
547 $size = $min_size + (($value - $min_qty) * $step);
548 // uncomment if you want sizes in whole %:
551 if(isset($this->tags[$key])) {
552 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
557 $output = substr($output, 0, strlen($output)-2);
560 } // getAvailableTags()
563 * output all selected tags
565 * this function output all tags which have been selected
566 * by the user. the selected tags are stored in the
567 * session-variable $_SESSION['selected_tags']
569 public function getSelectedTags()
574 foreach($this->avail_tags as $tag)
576 // return all selected tags
577 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
578 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
582 $output = substr($output, 0, strlen($output)-2);
585 } // getSelectedTags()
588 * add tag to users session variable
590 * this function will add the specified to users current
591 * tag selection. if a date search has been made before
592 * it will be now cleared
594 public function addTag($tag)
596 if(!isset($_SESSION['selected_tags']))
597 $_SESSION['selected_tags'] = Array();
599 if(!in_array($tag, $_SESSION['selected_tags']))
600 array_push($_SESSION['selected_tags'], $tag);
605 * remove tag to users session variable
607 * this function removes the specified tag from
608 * users current tag selection
610 public function delTag($tag)
612 if(isset($_SESSION['selected_tags'])) {
613 $key = array_search($tag, $_SESSION['selected_tags']);
614 unset($_SESSION['selected_tags'][$key]);
615 sort($_SESSION['selected_tags']);
621 * reset tag selection
623 * if there is any tag selection, it will be
626 public function resetTags()
628 if(isset($_SESSION['selected_tags']))
629 unset($_SESSION['selected_tags']);
636 * if a specific photo was requested (external link)
637 * unset the session variable now
639 public function resetPhotoView()
641 if(isset($_SESSION['current_photo']))
642 unset($_SESSION['current_photo']);
644 } // resetPhotoView();
649 * if any tag search has taken place, reset
652 public function resetTagSearch()
654 if(isset($_SESSION['searchfor']))
655 unset($_SESSION['searchfor']);
657 } // resetTagSearch()
662 * if any date search has taken place, reset
665 public function resetDateSearch()
667 if(isset($_SESSION['from_date']))
668 unset($_SESSION['from_date']);
669 if(isset($_SESSION['to_date']))
670 unset($_SESSION['to_date']);
672 } // resetDateSearch();
675 * return all photo according selection
677 * this function returns all photos based on
678 * the tag-selection, tag- or date-search.
679 * the tag-search also has to take care of AND
680 * and OR conjunctions
682 public function getPhotoSelection()
684 $matched_photos = Array();
686 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
687 $from_date = $_SESSION['from_date'];
688 $to_date = $_SESSION['to_date'];
689 $additional_where_cond = "
690 p.time>='". $from_date ."'
692 p.time<='". $to_date ."'
696 if(isset($_SESSION['sort_order'])) {
697 $order_str = $this->get_sort_order();
700 /* return a search result */
701 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
703 SELECT DISTINCT pt1.photo_id
705 INNER JOIN photo_tags pt2
706 ON pt1.photo_id=pt2.photo_id
713 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
715 if(isset($additional_where_cond))
716 $query_str.= "AND ". $additional_where_cond ." ";
718 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
719 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
722 if(isset($order_str))
723 $query_str.= $order_str;
725 $result = $this->db->db_query($query_str);
726 while($row = $this->db->db_fetch_object($result)) {
727 array_push($matched_photos, $row['photo_id']);
729 return $matched_photos;
732 /* return according the selected tags */
733 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
735 foreach($_SESSION['selected_tags'] as $tag)
736 $selected.= $tag .",";
737 $selected = substr($selected, 0, strlen($selected)-1);
739 /* photo has to match at least on of the selected tags */
740 if($_SESSION['tag_condition'] == 'or') {
742 SELECT DISTINCT pt1.photo_id
744 INNER JOIN photo_tags pt2
745 ON pt1.photo_id=pt2.photo_id
750 WHERE pt1.tag_id IN (". $selected .")
752 if(isset($additional_where_cond))
753 $query_str.= "AND ". $additional_where_cond ." ";
755 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
756 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
759 if(isset($order_str))
760 $query_str.= $order_str;
762 /* photo has to match all selected tags */
763 elseif($_SESSION['tag_condition'] == 'and') {
765 if(count($_SESSION['selected_tags']) >= 32) {
766 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
767 print "evaluate your tag selection. Please remove some tags from your selection.\n";
771 /* Join together a table looking like
773 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
775 so the query can quickly return all images matching the
776 selected tags in an AND condition
781 SELECT DISTINCT pt1.photo_id
785 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
792 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
794 INNER JOIN photo_tags pt". ($i+2) ."
795 ON pt1.photo_id=pt". ($i+2) .".photo_id
802 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
803 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
805 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
808 if(isset($additional_where_cond))
809 $query_str.= "AND ". $additional_where_cond;
811 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
812 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
815 if(isset($order_str))
816 $query_str.= $order_str;
820 $result = $this->db->db_query($query_str);
821 while($row = $this->db->db_fetch_object($result)) {
822 array_push($matched_photos, $row['photo_id']);
824 return $matched_photos;
827 /* return all available photos */
829 SELECT DISTINCT photo_id
836 if(isset($additional_where_cond))
837 $query_str.= "WHERE ". $additional_where_cond ." ";
839 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
840 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
843 if(isset($order_str))
844 $query_str.= $order_str;
846 $result = $this->db->db_query($query_str);
847 while($row = $this->db->db_fetch_object($result)) {
848 array_push($matched_photos, $row['photo_id']);
850 return $matched_photos;
852 } // getPhotoSelection()
855 * control HTML ouput for photo index
857 * this function provides all the necessary information
858 * for the photo index template.
860 public function showPhotoIndex()
862 $photos = $this->getPhotoSelection();
864 $count = count($photos);
866 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
867 $anchor = $_SESSION['begin_with'];
869 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
875 elseif($this->cfg->rows_per_page > 0) {
877 if(!$_SESSION['begin_with'] || $_SESSION['begin_with'] == 0)
881 $begin_with = $_SESSION['begin_with'];
883 // verify $begin_with - perhaps the thumbs-per-rows or
884 // rows-per-page variables have changed or the jump back
885 // from a photo wasn't exact - so calculate the real new
887 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
888 for($i = 0; $i <= $count; $i+=$multiplicator) {
889 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
896 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
902 $images[$rows] = Array();
903 $img_height[$rows] = Array();
904 $img_width[$rows] = Array();
905 $img_id[$rows] = Array();
906 $img_name[$rows] = Array();
907 $img_title = Array();
909 for($i = $begin_with; $i < $end_with; $i++) {
911 $images[$rows][$cols] = $photos[$i];
912 $img_id[$rows][$cols] = $i;
913 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
914 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
916 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
918 if(file_exists($thumb_path)) {
919 $info = getimagesize($thumb_path);
920 $img_width[$rows][$cols] = $info[0];
921 $img_height[$rows][$cols] = $info[1];
924 if($cols == $this->cfg->thumbs_per_row-1) {
927 $images[$rows] = Array();
928 $img_width[$rows] = Array();
929 $img_height[$rows] = Array();
936 // +1 for for smarty's selection iteration
939 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
940 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
942 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
943 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
944 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
947 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
948 $this->tmpl->assign('tag_result', 1);
951 /* do we have to display the page selector ? */
952 if($this->cfg->rows_per_page != 0) {
954 /* calculate the page switchers */
955 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
956 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
959 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
960 if($end_with < $count)
961 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
963 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
964 $last_page = ceil($count / $photo_per_page);
966 /* get the current selected page */
967 if($begin_with == 0) {
971 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
978 for($i = 1; $i <= $last_page; $i++) {
980 if($current_page == $i)
981 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
982 elseif($current_page-1 == $i || $current_page+1 == $i)
983 $style = "style=\"font-size: 105%;\"";
984 elseif(($current_page-5 >= $i) && ($i != 1) ||
985 ($current_page+5 <= $i) && ($i != $last_page))
986 $style = "style=\"font-size: 75%;\"";
990 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
993 $select.= ">". $i ."</a> ";
995 // until 9 pages we show the selector from 1-9
996 if($last_page <= 9) {
997 $page_select.= $select;
1000 if($i == 1 /* first page */ ||
1001 $i == $last_page /* last page */ ||
1002 $i == $current_page /* current page */ ||
1003 $i == ceil($last_page * 0.25) /* first quater */ ||
1004 $i == ceil($last_page * 0.5) /* half */ ||
1005 $i == ceil($last_page * 0.75) /* third quater */ ||
1006 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1007 (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 */ ||
1008 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1009 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1011 $page_select.= $select;
1019 $page_select.= "......... ";
1024 /* only show the page selector if we have more then one page */
1026 $this->tmpl->assign('page_selector', $page_select);
1030 $current_tags = $this->getCurrentTags();
1031 $extern_link = "index.php?mode=showpi";
1032 $rss_link = "index.php?mode=rss";
1033 if($current_tags != "") {
1034 $extern_link.= "&tags=". $current_tags;
1035 $rss_link.= "&tags=". $current_tags;
1037 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1038 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1039 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1042 $export_link = "index.php?mode=export";
1043 $slideshow_link = "index.php?mode=slideshow";
1045 $this->tmpl->assign('extern_link', $extern_link);
1046 $this->tmpl->assign('slideshow_link', $slideshow_link);
1047 $this->tmpl->assign('export_link', $export_link);
1048 $this->tmpl->assign('rss_link', $rss_link);
1049 $this->tmpl->assign('count', $count);
1050 $this->tmpl->assign('width', $this->cfg->thumb_width);
1051 $this->tmpl->assign('images', $images);
1052 $this->tmpl->assign('img_width', $img_width);
1053 $this->tmpl->assign('img_height', $img_height);
1054 $this->tmpl->assign('img_id', $img_id);
1055 $this->tmpl->assign('img_name', $img_name);
1056 $this->tmpl->assign('img_title', $img_title);
1057 $this->tmpl->assign('rows', $rows);
1058 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1060 $this->tmpl->show("photo_index.tpl");
1063 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1065 } // showPhotoIndex()
1068 * show credit template
1070 public function showCredits()
1072 $this->tmpl->assign('version', $this->cfg->version);
1073 $this->tmpl->assign('product', $this->cfg->product);
1074 $this->tmpl->assign('db_version', $this->dbver);
1075 $this->tmpl->show("credits.tpl");
1080 * create_thumbnails for the requested width
1082 * this function creates image thumbnails of $orig_image
1083 * stored as $thumb_image. It will check if the image is
1084 * in a supported format, if necessary rotate the image
1085 * (based on EXIF orientation meta headers) and re-sizing.
1087 public function create_thumbnail($orig_image, $thumb_image, $width)
1089 if(!file_exists($orig_image)) {
1093 $details = getimagesize($orig_image);
1095 /* check if original photo is a support image type */
1096 if(!$this->checkifImageSupported($details['mime']))
1099 $meta = $this->get_meta_informations($orig_image);
1104 switch($meta['Orientation']) {
1106 case 1: /* top, left */
1107 $rotate = 0; $flip = false; break;
1108 case 2: /* top, right */
1109 $rotate = 0; $flip = true; break;
1110 case 3: /* bottom, left */
1111 $rotate = 180; $flip = false; break;
1112 case 4: /* bottom, right */
1113 $rotate = 180; $flip = true; break;
1114 case 5: /* left side, top */
1115 $rotate = 90; $flip = true; break;
1116 case 6: /* right side, top */
1117 $rotate = 90; $flip = false; break;
1118 case 7: /* left side, bottom */
1119 $rotate = 270; $flip = true; break;
1120 case 8: /* right side, bottom */
1121 $rotate = 270; $flip = false; break;
1124 $src_img = @imagecreatefromjpeg($orig_image);
1127 print "Can't load image from ". $orig_image ."\n";
1131 /* grabs the height and width */
1132 $cur_width = imagesx($src_img);
1133 $cur_height = imagesy($src_img);
1135 // If requested width is more then the actual image width,
1136 // do not generate a thumbnail, instead safe the original
1137 // as thumbnail but with lower quality
1139 if($width >= $cur_width) {
1140 $result = imagejpeg($src_img, $thumb_image, 75);
1141 imagedestroy($src_img);
1145 // If the image will be rotate because EXIF orientation said so
1146 // 'virtually rotate' the image for further calculations
1147 if($rotate == 90 || $rotate == 270) {
1149 $cur_width = $cur_height;
1153 /* calculates aspect ratio */
1154 $aspect_ratio = $cur_height / $cur_width;
1157 if($aspect_ratio < 1) {
1159 $new_h = abs($new_w * $aspect_ratio);
1161 /* 'virtually' rotate the image and calculate it's ratio */
1162 $tmp_w = $cur_height;
1163 $tmp_h = $cur_width;
1164 /* now get the ratio from the 'rotated' image */
1165 $tmp_ratio = $tmp_h/$tmp_w;
1166 /* now calculate the new dimensions */
1168 $tmp_h = abs($tmp_w * $tmp_ratio);
1170 // now that we know, how high they photo should be, if it
1171 // gets rotated, use this high to scale the image
1173 $new_w = abs($new_h / $aspect_ratio);
1175 // If the image will be rotate because EXIF orientation said so
1176 // now 'virtually rotate' back the image for the image manipulation
1177 if($rotate == 90 || $rotate == 270) {
1184 /* creates new image of that size */
1185 $dst_img = imagecreatetruecolor($new_w, $new_h);
1187 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1189 /* copies resized portion of original image into new image */
1190 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1192 /* needs the image to be flipped horizontal? */
1196 for($x = 0; $x < $new_w; $x++) {
1197 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1202 $this->_debug("(ROTATE)");
1203 $dst_img = $this->rotateImage($dst_img, $rotate);
1206 /* write down new generated file */
1207 $result = imagejpeg($dst_img, $thumb_image, 75);
1209 /* free your mind */
1210 imagedestroy($dst_img);
1211 imagedestroy($src_img);
1213 if($result === false) {
1214 print "Can't write thumbnail ". $thumb_image ."\n";
1220 } // create_thumbnail()
1223 * return all exif meta data from the file
1225 public function get_meta_informations($file)
1227 return exif_read_data($file);
1229 } // get_meta_informations()
1232 * create phpfspot own sqlite database
1234 * this function creates phpfspots own sqlite database
1235 * if it does not exist yet. this own is used to store
1236 * some necessary informations (md5 sum's, ...).
1238 public function check_config_table()
1240 // if the config table doesn't exist yet, create it
1241 if(!$this->cfg_db->db_check_table_exists("images")) {
1242 $this->cfg_db->db_exec("
1243 CREATE TABLE images (
1244 img_idx int primary key,
1250 } // check_config_table
1253 * Generates a thumbnail from photo idx
1255 * This function will generate JPEG thumbnails from provided F-Spot photo
1258 * 1. Check if all thumbnail generations (width) are already in place and
1260 * 2. Check if the md5sum of the original file has changed
1261 * 3. Generate the thumbnails if needed
1263 public function gen_thumb($idx = 0, $force = 0)
1267 $resolutions = Array(
1268 $this->cfg->thumb_width,
1269 $this->cfg->photo_width,
1270 $this->cfg->mini_width,
1273 /* get details from F-Spot's database */
1274 $details = $this->get_photo_details($idx);
1276 /* calculate file MD5 sum */
1277 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1279 if(!file_exists($full_path)) {
1280 $this->_error("File ". $full_path ." does not exist\n");
1284 if(!is_readable($full_path)) {
1285 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1289 $file_md5 = md5_file($full_path);
1291 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1295 foreach($resolutions as $resolution) {
1297 $thumb_sub_path = substr($file_md5, 0, 2);
1298 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1300 if(!file_exists(dirname($thumb_path))) {
1301 mkdir(dirname($thumb_path), 0755);
1304 /* if the thumbnail file doesn't exist, create it */
1305 if(!file_exists($thumb_path)) {
1307 $this->_debug(" ". $resolution ."px");
1308 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1313 /* if the file hasn't changed there is no need to regen the thumb */
1314 elseif($file_md5 != $this->getMD5($idx) || $force) {
1316 $this->_debug(" ". $resolution ."px");
1317 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1325 $this->_debug(" already exist");
1328 /* set the new/changed MD5 sum for the current photo */
1330 $this->setMD5($idx, $file_md5);
1333 $this->_debug("\n");
1338 * returns stored md5 sum for a specific photo
1340 * this function queries the phpfspot database for a
1341 * stored MD5 checksum of the specified photo
1343 public function getMD5($idx)
1345 $result = $this->cfg_db->db_query("
1348 WHERE img_idx='". $idx ."'
1354 $img = $this->cfg_db->db_fetch_object($result);
1355 return $img['img_md5'];
1360 * set MD5 sum for the specific photo
1362 private function setMD5($idx, $md5)
1364 $result = $this->cfg_db->db_exec("
1365 REPLACE INTO images (img_idx, img_md5)
1366 VALUES ('". $idx ."', '". $md5 ."')
1372 * store current tag condition
1374 * this function stores the current tag condition
1375 * (AND or OR) in the users session variables
1377 public function setTagCondition($mode)
1379 $_SESSION['tag_condition'] = $mode;
1381 } // setTagCondition()
1384 * invoke tag & date search
1386 * this function will return all matching tags and store
1387 * them in the session variable selected_tags. furthermore
1388 * it also handles the date search.
1389 * getPhotoSelection() will then only return the matching
1392 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1396 $_SESSION['searchfor'] = $searchfor;
1397 $_SESSION['sort_order'] = $sort_order;
1399 $_SESSION['from_date'] = strtotime($from);
1401 unset($_SESSION['from_date']);
1403 $_SESSION['to_date'] = strtotime($to);
1405 unset($_SESSION['to_date']);
1407 if($searchfor != "") {
1408 /* new search, reset the current selected tags */
1409 $_SESSION['selected_tags'] = Array();
1410 foreach($this->avail_tags as $tag) {
1411 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1412 array_push($_SESSION['selected_tags'], $tag);
1421 * this function rotates the image according the
1424 private function rotateImage($img, $degrees)
1426 if(function_exists("imagerotate")) {
1427 $img = imagerotate($img, $degrees, 0);
1429 function imagerotate($src_img, $angle)
1431 $src_x = imagesx($src_img);
1432 $src_y = imagesy($src_img);
1433 if ($angle == 180) {
1437 elseif ($src_x <= $src_y) {
1441 elseif ($src_x >= $src_y) {
1446 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1447 imagealphablending($rotate, false);
1452 for ($y = 0; $y < ($src_y); $y++) {
1453 for ($x = 0; $x < ($src_x); $x++) {
1454 $color = imagecolorat($src_img, $x, $y);
1455 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1461 for ($y = 0; $y < ($src_y); $y++) {
1462 for ($x = 0; $x < ($src_x); $x++) {
1463 $color = imagecolorat($src_img, $x, $y);
1464 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1470 for ($y = 0; $y < ($src_y); $y++) {
1471 for ($x = 0; $x < ($src_x); $x++) {
1472 $color = imagecolorat($src_img, $x, $y);
1473 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1487 $img = imagerotate($img, $degrees);
1496 * return all assigned tags for the specified photo
1498 private function get_photo_tags($idx)
1500 $result = $this->db->db_query("
1503 INNER JOIN photo_tags pt
1505 WHERE pt.photo_id='". $idx ."'
1510 while($row = $this->db->db_fetch_object($result))
1511 $tags[$row['id']] = $row['name'];
1515 } // get_photo_tags()
1518 * create on-the-fly images with text within
1520 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1522 if (strlen($color) != 6)
1525 $int = hexdec($color);
1526 $h = imagefontheight($font);
1527 $fw = imagefontwidth($font);
1528 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1529 $lines = count($txt);
1530 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1531 $bg = imagecolorallocate($im, 255, 255, 255);
1532 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1535 foreach ($txt as $text) {
1536 $x = (($w - ($fw * strlen($text))) / 2);
1537 imagestring($im, $font, $x, $y, $text, $color);
1538 $y += ($h + $space);
1541 Header("Content-type: image/png");
1544 } // showTextImage()
1547 * check if all requirements are met
1549 private function checkRequirements()
1551 if(!function_exists("imagecreatefromjpeg")) {
1552 print "PHP GD library extension is missing<br />\n";
1556 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1557 print "PHP SQLite3 library extension is missing<br />\n";
1561 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1562 ini_set('track_errors', 1);
1563 @include_once 'HTML/AJAX/Server.php';
1564 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1565 print "PEAR HTML_AJAX package is missing<br />\n";
1568 @include_once 'Calendar/Calendar.php';
1569 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1570 print "PEAR Calendar package is missing<br />\n";
1573 ini_restore('track_errors');
1580 } // checkRequirements()
1582 private function _debug($text)
1584 if($this->fromcmd) {
1591 * check if specified MIME type is supported
1593 public function checkifImageSupported($mime)
1595 if(in_array($mime, Array("image/jpeg")))
1600 } // checkifImageSupported()
1602 public function _error($text)
1604 switch($this->cfg->logging) {
1606 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1613 error_log($text, 3, $his->cfg->log_file);
1620 * output calendard input fields
1622 private function get_calendar($mode)
1624 $year = $_SESSION[$mode .'_date'] ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1625 $month = $_SESSION[$mode .'_date'] ? date("m", $_SESSION[$mode .'_date']) : date("m");
1626 $day = $_SESSION[$mode .'_date'] ? date("d", $_SESSION[$mode .'_date']) : date("d");
1628 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1629 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1631 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1632 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1634 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1635 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1642 * output calendar matrix
1644 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1646 if (!isset($year)) $year = date('Y');
1647 if (!isset($month)) $month = date('m');
1648 if (!isset($day)) $day = date('d');
1653 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1654 require_once CALENDAR_ROOT.'Day.php';
1657 $month = new Calendar_Month_Weekdays($year,$month);
1660 $prevStamp = $month->prevMonth(true);
1661 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1662 $nextStamp = $month->nextMonth(true);
1663 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1665 $selectedDays = array (
1666 new Calendar_Day($year,$month,$day),
1667 new Calendar_Day($year,12,25),
1670 // Build the days in the month
1671 $month->build($selectedDays);
1673 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1674 $this->tmpl->assign('prev_month', $prev);
1675 $this->tmpl->assign('next_month', $next);
1677 while ( $day = $month->fetch() ) {
1679 if(!isset($matrix[$rows]))
1680 $matrix[$rows] = Array();
1684 $dayStamp = $day->thisDay(true);
1685 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1687 // isFirst() to find start of week
1688 if ( $day->isFirst() )
1691 if ( $day->isSelected() ) {
1692 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1693 } else if ( $day->isEmpty() ) {
1694 $string.= "<td> </td>\n";
1696 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1699 // isLast() to find end of week
1700 if ( $day->isLast() )
1701 $string.= "</tr>\n";
1703 $matrix[$rows][$cols] = $string;
1713 $this->tmpl->assign('matrix', $matrix);
1714 $this->tmpl->assign('rows', $rows);
1715 $this->tmpl->show("calendar.tpl");
1717 } // get_calendar_matrix()
1720 * output export page
1722 public function getExport($mode)
1724 $pictures = $this->getPhotoSelection();
1725 $current_tags = $this->getCurrentTags();
1727 foreach($pictures as $picture) {
1729 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1730 if($current_tags != "") {
1731 $orig_url.= "&tags=". $current_tags;
1733 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1734 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1737 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1742 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1743 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1747 // "[%pictureurl% %thumbnailurl%]"
1748 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1751 case 'MoinMoinList':
1752 // " * [%pictureurl% %thumbnailurl%]"
1753 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1764 public function getRSSFeed()
1766 Header("Content-type: text/xml; charset=utf-8");
1767 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1770 xmlns:media="http://search.yahoo.com/mrss/"
1771 xmlns:dc="http://purl.org/dc/elements/1.1/"
1774 <title>phpfspot</title>
1775 <description>phpfspot RSS feed</description>
1776 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1777 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1778 <generator>phpfspot</generator>
1781 $pictures = $this->getPhotoSelection();
1782 $current_tags = $this->getCurrentTags();
1784 foreach($pictures as $picture) {
1786 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1787 if($current_tags != "") {
1788 $orig_url.= "&tags=". $current_tags;
1790 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1791 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1794 $details = $this->get_photo_details($picture);
1796 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1797 $thumb_html = htmlspecialchars("
1798 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1800 ". $details['description']);
1802 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1803 $meta = $this->get_meta_informations($orig_path);
1804 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1808 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1809 <link><?php print htmlspecialchars($orig_url); ?></link>
1810 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1811 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1813 <?php print $thumb_html; ?>
1815 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1830 * return all selected tags as one string
1832 private function getCurrentTags()
1835 if($_SESSION['selected_tags'] != "") {
1836 foreach($_SESSION['selected_tags'] as $tag)
1837 $current_tags.= $tag .",";
1838 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1840 return $current_tags;
1842 } // getCurrentTags()
1845 * return the current photo
1847 public function getCurrentPhoto()
1849 if(isset($_SESSION['current_photo'])) {
1850 print $_SESSION['current_photo'];
1852 } // getCurrentPhoto()
1855 * tells the client browser what to do
1857 * this function is getting called via AJAX by the
1858 * client browsers. it will tell them what they have
1859 * to do next. This is necessary for directly jumping
1860 * into photo index or single photo view when the are
1861 * requested with specific URLs
1863 public function whatToDo()
1865 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1866 return "show_photo";
1868 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1869 return "showpi_tags";
1871 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1875 return "nothing special";
1880 * return the current process-user
1882 private function getuid()
1884 if($uid = posix_getuid()) {
1885 if($user = posix_getpwuid($uid)) {
1886 return $user['name'];
1895 * returns a select-dropdown box to select photo index sort parameters
1897 private function get_sort_field()
1899 $output = "<select name=\"sort_order\">";
1900 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1901 $output.= "<option value=\"". $sort_order ."\"";
1902 if($sort_order == $_SESSION['sort_order']) {
1903 $output.= " selected=\"selected\"";
1905 $output.= ">". $sort_order ."</option>";
1907 $output.= "</select>";
1910 } // get_sort_field()
1913 * returns the currently selected sort order
1915 private function get_sort_order()
1917 switch($_SESSION['sort_order']) {
1919 return " ORDER BY p.time ASC";
1922 return " ORDER BY p.time DESC";
1925 return " ORDER BY p.name ASC";
1928 return " ORDER BY p.name DESC";
1932 } // get_sort_order()
1935 * return the next to be shown slide show image
1937 * this function returns the URL of the next image
1938 * in the slideshow sequence.
1940 public function getNextSlideShowImage()
1942 $all_photos = $this->getPhotoSelection();
1944 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1945 $_SESSION['slideshow_img'] = 0;
1947 $_SESSION['slideshow_img']++;
1949 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1951 } // getNextSlideShowImage()
1954 * return the previous to be shown slide show image
1956 * this function returns the URL of the previous image
1957 * in the slideshow sequence.
1959 public function getPrevSlideShowImage()
1961 $all_photos = $this->getPhotoSelection();
1963 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1964 $_SESSION['slideshow_img'] = 0;
1966 $_SESSION['slideshow_img']--;
1968 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1970 } // getPrevSlideShowImage()
1972 public function resetSlideShow()
1974 if(isset($_SESSION['slideshow_img']))
1975 unset($_SESSION['slideshow_img']);
1976 } // resetSlideShow()
1981 * this function will get all photos from the fspot
1982 * database and randomly return ONE entry
1984 * saddly there is yet no sqlite3 function which returns
1985 * the bulk result in array, so we have to fill up our
1988 public function get_random_photo()
1992 $result = $this->db->db_query("
1997 while($row = $this->db->db_fetch_object($result)) {
1998 array_push($all, $row['id']);
2001 return $all[array_rand($all)];
2003 } // get_random_photo()
2006 * validates provided date
2008 * this function validates if the provided date
2009 * contains a valid date and will return true
2012 public function isValidDate($date_str)
2014 $timestamp = strtotime($date_str);
2016 if(is_numeric($timestamp))
2024 * timestamp to string conversion
2026 private function ts2str($timestamp)
2028 return strftime("%Y-%m-%d", $timestamp);
2031 private function extractTags($tags_str)
2033 $not_validated = split(',', $_GET['tags']);
2034 $validated = array();
2036 foreach($not_validated as $tag) {
2037 if(is_numeric($tag))
2038 array_push($validated, $tag);
2046 * returns the full path to a thumbnail
2048 public function get_thumb_path($width, $photo)
2050 $md5 = $this->getMD5($photo);
2051 $sub_path = substr($md5, 0, 2);
2052 return $this->cfg->thumb_path
2060 } // get_thumb_path()
2063 * returns server's virtual host name
2065 private function get_server_name()
2067 return $_SERVER['SERVER_NAME'];
2068 } // get_server_name()
2071 * returns type of webprotocol which is
2074 private function get_web_protocol()
2076 if(!isset($_SERVER['HTTPS']))
2080 } // get_web_protocol()
2083 * return url to this phpfspot installation
2085 private function get_phpfspot_url()
2087 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2088 } // get_phpfspot_url()
2091 * check file exists and is readable
2093 * returns true, if everything is ok, otherwise false
2094 * if $silent is not set, this function will output and
2097 private function check_readable($file, $silent = null)
2099 if(!file_exists($file)) {
2101 print "File \"". $file ."\" does not exist.\n";
2105 if(!is_readable($file)) {
2107 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2113 } // check_readable()
2116 * check if all needed indices are present
2118 * this function checks, if some needed indices are already
2119 * present, or if not, create them on the fly. they are
2120 * necessary to speed up some queries like that one look for
2121 * all tags, when show_tags is specified in the configuration.
2123 private function checkDbIndices()
2125 $result = $this->db->db_exec("
2126 CREATE INDEX IF NOT EXISTS
2133 } // checkDbIndices()
2136 * retrive F-Spot database version
2138 * this function will return the F-Spot database version number
2139 * It is stored within the sqlite3 database in the table meta
2141 public function getFspotDBVersion()
2143 if($result = $this->db->db_fetchSingleRow("
2144 SELECT data as version
2147 name LIKE 'F-Spot Database Version'
2149 return $result['version'];
2153 } // getFspotDBVersion()
2156 * parse the provided URI and will returned the
2159 public function parse_uri($uri, $mode)
2161 if(($components = parse_url($uri)) !== false) {
2165 return basename($components['path']);
2168 return dirname($components['path']);
2171 return $components['path'];