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:");
1293 foreach($resolutions as $resolution) {
1295 $thumb_sub_path = substr($file_md5, 0, 2);
1296 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1298 if(!file_exists(dirname($thumb_path))) {
1299 mkdir(dirname($thumb_path), 0755);
1302 /* if the thumbnail file doesn't exist, create it */
1303 if(!file_exists($thumb_path)) {
1305 $this->_debug(" ". $resolution ."px");
1306 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1309 /* if the file hasn't changed there is no need to regen the thumb */
1310 elseif($file_md5 != $this->getMD5($idx) || $force) {
1312 $this->_debug(" ". $resolution ."px");
1313 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1319 /* set the new/changed MD5 sum for the current photo */
1321 $this->setMD5($idx, $file_md5);
1324 $this->_debug("\n");
1329 * returns stored md5 sum for a specific photo
1331 * this function queries the phpfspot database for a
1332 * stored MD5 checksum of the specified photo
1334 public function getMD5($idx)
1336 $result = $this->cfg_db->db_query("
1339 WHERE img_idx='". $idx ."'
1345 $img = $this->cfg_db->db_fetch_object($result);
1346 return $img['img_md5'];
1351 * set MD5 sum for the specific photo
1353 private function setMD5($idx, $md5)
1355 $result = $this->cfg_db->db_exec("
1356 REPLACE INTO images (img_idx, img_md5)
1357 VALUES ('". $idx ."', '". $md5 ."')
1363 * store current tag condition
1365 * this function stores the current tag condition
1366 * (AND or OR) in the users session variables
1368 public function setTagCondition($mode)
1370 $_SESSION['tag_condition'] = $mode;
1372 } // setTagCondition()
1375 * invoke tag & date search
1377 * this function will return all matching tags and store
1378 * them in the session variable selected_tags. furthermore
1379 * it also handles the date search.
1380 * getPhotoSelection() will then only return the matching
1383 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1387 $_SESSION['searchfor'] = $searchfor;
1388 $_SESSION['sort_order'] = $sort_order;
1390 $_SESSION['from_date'] = strtotime($from);
1392 unset($_SESSION['from_date']);
1394 $_SESSION['to_date'] = strtotime($to);
1396 unset($_SESSION['to_date']);
1398 if($searchfor != "") {
1399 /* new search, reset the current selected tags */
1400 $_SESSION['selected_tags'] = Array();
1401 foreach($this->avail_tags as $tag) {
1402 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1403 array_push($_SESSION['selected_tags'], $tag);
1412 * this function rotates the image according the
1415 private function rotateImage($img, $degrees)
1417 if(function_exists("imagerotate")) {
1418 $img = imagerotate($img, $degrees, 0);
1420 function imagerotate($src_img, $angle)
1422 $src_x = imagesx($src_img);
1423 $src_y = imagesy($src_img);
1424 if ($angle == 180) {
1428 elseif ($src_x <= $src_y) {
1432 elseif ($src_x >= $src_y) {
1437 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1438 imagealphablending($rotate, false);
1443 for ($y = 0; $y < ($src_y); $y++) {
1444 for ($x = 0; $x < ($src_x); $x++) {
1445 $color = imagecolorat($src_img, $x, $y);
1446 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
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, $y, $dest_y - $x - 1, $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, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1478 $img = imagerotate($img, $degrees);
1487 * return all assigned tags for the specified photo
1489 private function get_photo_tags($idx)
1491 $result = $this->db->db_query("
1494 INNER JOIN photo_tags pt
1496 WHERE pt.photo_id='". $idx ."'
1501 while($row = $this->db->db_fetch_object($result))
1502 $tags[$row['id']] = $row['name'];
1506 } // get_photo_tags()
1509 * create on-the-fly images with text within
1511 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1513 if (strlen($color) != 6)
1516 $int = hexdec($color);
1517 $h = imagefontheight($font);
1518 $fw = imagefontwidth($font);
1519 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1520 $lines = count($txt);
1521 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1522 $bg = imagecolorallocate($im, 255, 255, 255);
1523 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1526 foreach ($txt as $text) {
1527 $x = (($w - ($fw * strlen($text))) / 2);
1528 imagestring($im, $font, $x, $y, $text, $color);
1529 $y += ($h + $space);
1532 Header("Content-type: image/png");
1535 } // showTextImage()
1538 * check if all requirements are met
1540 private function checkRequirements()
1542 if(!function_exists("imagecreatefromjpeg")) {
1543 print "PHP GD library extension is missing<br />\n";
1547 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1548 print "PHP SQLite3 library extension is missing<br />\n";
1552 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1553 ini_set('track_errors', 1);
1554 @include_once 'HTML/AJAX/Server.php';
1555 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1556 print "PEAR HTML_AJAX package is missing<br />\n";
1559 @include_once 'Calendar/Calendar.php';
1560 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1561 print "PEAR Calendar package is missing<br />\n";
1564 ini_restore('track_errors');
1571 } // checkRequirements()
1573 private function _debug($text)
1575 if($this->fromcmd) {
1582 * check if specified MIME type is supported
1584 public function checkifImageSupported($mime)
1586 if(in_array($mime, Array("image/jpeg")))
1591 } // checkifImageSupported()
1593 public function _error($text)
1595 switch($this->cfg->logging) {
1597 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1604 error_log($text, 3, $his->cfg->log_file);
1611 * output calendard input fields
1613 private function get_calendar($mode)
1615 $year = $_SESSION[$mode .'_date'] ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1616 $month = $_SESSION[$mode .'_date'] ? date("m", $_SESSION[$mode .'_date']) : date("m");
1617 $day = $_SESSION[$mode .'_date'] ? date("d", $_SESSION[$mode .'_date']) : date("d");
1619 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1620 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1622 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1623 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1625 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1626 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1633 * output calendar matrix
1635 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1637 if (!isset($year)) $year = date('Y');
1638 if (!isset($month)) $month = date('m');
1639 if (!isset($day)) $day = date('d');
1644 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1645 require_once CALENDAR_ROOT.'Day.php';
1648 $month = new Calendar_Month_Weekdays($year,$month);
1651 $prevStamp = $month->prevMonth(true);
1652 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1653 $nextStamp = $month->nextMonth(true);
1654 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1656 $selectedDays = array (
1657 new Calendar_Day($year,$month,$day),
1658 new Calendar_Day($year,12,25),
1661 // Build the days in the month
1662 $month->build($selectedDays);
1664 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1665 $this->tmpl->assign('prev_month', $prev);
1666 $this->tmpl->assign('next_month', $next);
1668 while ( $day = $month->fetch() ) {
1670 if(!isset($matrix[$rows]))
1671 $matrix[$rows] = Array();
1675 $dayStamp = $day->thisDay(true);
1676 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1678 // isFirst() to find start of week
1679 if ( $day->isFirst() )
1682 if ( $day->isSelected() ) {
1683 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1684 } else if ( $day->isEmpty() ) {
1685 $string.= "<td> </td>\n";
1687 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1690 // isLast() to find end of week
1691 if ( $day->isLast() )
1692 $string.= "</tr>\n";
1694 $matrix[$rows][$cols] = $string;
1704 $this->tmpl->assign('matrix', $matrix);
1705 $this->tmpl->assign('rows', $rows);
1706 $this->tmpl->show("calendar.tpl");
1708 } // get_calendar_matrix()
1711 * output export page
1713 public function getExport($mode)
1715 $pictures = $this->getPhotoSelection();
1716 $current_tags = $this->getCurrentTags();
1718 foreach($pictures as $picture) {
1720 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1721 if($current_tags != "") {
1722 $orig_url.= "&tags=". $current_tags;
1724 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1725 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1728 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1733 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1734 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1738 // "[%pictureurl% %thumbnailurl%]"
1739 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1742 case 'MoinMoinList':
1743 // " * [%pictureurl% %thumbnailurl%]"
1744 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1755 public function getRSSFeed()
1757 Header("Content-type: text/xml; charset=utf-8");
1758 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1761 xmlns:media="http://search.yahoo.com/mrss/"
1762 xmlns:dc="http://purl.org/dc/elements/1.1/"
1765 <title>phpfspot</title>
1766 <description>phpfspot RSS feed</description>
1767 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1768 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1769 <generator>phpfspot</generator>
1772 $pictures = $this->getPhotoSelection();
1773 $current_tags = $this->getCurrentTags();
1775 foreach($pictures as $picture) {
1777 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1778 if($current_tags != "") {
1779 $orig_url.= "&tags=". $current_tags;
1781 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1782 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1785 $details = $this->get_photo_details($picture);
1787 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1788 $thumb_html = htmlspecialchars("
1789 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1791 ". $details['description']);
1793 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1794 $meta = $this->get_meta_informations($orig_path);
1795 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1799 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1800 <link><?php print htmlspecialchars($orig_url); ?></link>
1801 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1802 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1804 <?php print $thumb_html; ?>
1806 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1821 * return all selected tags as one string
1823 private function getCurrentTags()
1826 if($_SESSION['selected_tags'] != "") {
1827 foreach($_SESSION['selected_tags'] as $tag)
1828 $current_tags.= $tag .",";
1829 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1831 return $current_tags;
1833 } // getCurrentTags()
1836 * return the current photo
1838 public function getCurrentPhoto()
1840 if(isset($_SESSION['current_photo'])) {
1841 print $_SESSION['current_photo'];
1843 } // getCurrentPhoto()
1846 * tells the client browser what to do
1848 * this function is getting called via AJAX by the
1849 * client browsers. it will tell them what they have
1850 * to do next. This is necessary for directly jumping
1851 * into photo index or single photo view when the are
1852 * requested with specific URLs
1854 public function whatToDo()
1856 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1857 return "show_photo";
1859 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1860 return "showpi_tags";
1862 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1866 return "nothing special";
1871 * return the current process-user
1873 private function getuid()
1875 if($uid = posix_getuid()) {
1876 if($user = posix_getpwuid($uid)) {
1877 return $user['name'];
1886 * returns a select-dropdown box to select photo index sort parameters
1888 private function get_sort_field()
1890 $output = "<select name=\"sort_order\">";
1891 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1892 $output.= "<option value=\"". $sort_order ."\"";
1893 if($sort_order == $_SESSION['sort_order']) {
1894 $output.= " selected=\"selected\"";
1896 $output.= ">". $sort_order ."</option>";
1898 $output.= "</select>";
1901 } // get_sort_field()
1904 * returns the currently selected sort order
1906 private function get_sort_order()
1908 switch($_SESSION['sort_order']) {
1910 return " ORDER BY p.time ASC";
1913 return " ORDER BY p.time DESC";
1916 return " ORDER BY p.name ASC";
1919 return " ORDER BY p.name DESC";
1923 } // get_sort_order()
1926 * return the next to be shown slide show image
1928 * this function returns the URL of the next image
1929 * in the slideshow sequence.
1931 public function getNextSlideShowImage()
1933 $all_photos = $this->getPhotoSelection();
1935 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1936 $_SESSION['slideshow_img'] = 0;
1938 $_SESSION['slideshow_img']++;
1940 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1942 } // getNextSlideShowImage()
1945 * return the previous to be shown slide show image
1947 * this function returns the URL of the previous image
1948 * in the slideshow sequence.
1950 public function getPrevSlideShowImage()
1952 $all_photos = $this->getPhotoSelection();
1954 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1955 $_SESSION['slideshow_img'] = 0;
1957 $_SESSION['slideshow_img']--;
1959 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1961 } // getPrevSlideShowImage()
1963 public function resetSlideShow()
1965 if(isset($_SESSION['slideshow_img']))
1966 unset($_SESSION['slideshow_img']);
1967 } // resetSlideShow()
1972 * this function will get all photos from the fspot
1973 * database and randomly return ONE entry
1975 * saddly there is yet no sqlite3 function which returns
1976 * the bulk result in array, so we have to fill up our
1979 public function get_random_photo()
1983 $result = $this->db->db_query("
1988 while($row = $this->db->db_fetch_object($result)) {
1989 array_push($all, $row['id']);
1992 return $all[array_rand($all)];
1994 } // get_random_photo()
1997 * validates provided date
1999 * this function validates if the provided date
2000 * contains a valid date and will return true
2003 public function isValidDate($date_str)
2005 $timestamp = strtotime($date_str);
2007 if(is_numeric($timestamp))
2015 * timestamp to string conversion
2017 private function ts2str($timestamp)
2019 return strftime("%Y-%m-%d", $timestamp);
2022 private function extractTags($tags_str)
2024 $not_validated = split(',', $_GET['tags']);
2025 $validated = array();
2027 foreach($not_validated as $tag) {
2028 if(is_numeric($tag))
2029 array_push($validated, $tag);
2037 * returns the full path to a thumbnail
2039 public function get_thumb_path($width, $photo)
2041 $md5 = $this->getMD5($photo);
2042 $sub_path = substr($md5, 0, 2);
2043 return $this->cfg->thumb_path
2051 } // get_thumb_path()
2054 * returns server's virtual host name
2056 private function get_server_name()
2058 return $_SERVER['SERVER_NAME'];
2059 } // get_server_name()
2062 * returns type of webprotocol which is
2065 private function get_web_protocol()
2067 if(!isset($_SERVER['HTTPS']))
2071 } // get_web_protocol()
2074 * return url to this phpfspot installation
2076 private function get_phpfspot_url()
2078 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2079 } // get_phpfspot_url()
2082 * check file exists and is readable
2084 * returns true, if everything is ok, otherwise false
2085 * if $silent is not set, this function will output and
2088 private function check_readable($file, $silent = null)
2090 if(!file_exists($file)) {
2092 print "File \"". $file ."\" does not exist.\n";
2096 if(!is_readable($file)) {
2098 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2104 } // check_readable()
2107 * check if all needed indices are present
2109 * this function checks, if some needed indices are already
2110 * present, or if not, create them on the fly. they are
2111 * necessary to speed up some queries like that one look for
2112 * all tags, when show_tags is specified in the configuration.
2114 private function checkDbIndices()
2116 $result = $this->db->db_exec("
2117 CREATE INDEX IF NOT EXISTS
2124 } // checkDbIndices()
2127 * retrive F-Spot database version
2129 * this function will return the F-Spot database version number
2130 * It is stored within the sqlite3 database in the table meta
2132 public function getFspotDBVersion()
2134 if($result = $this->db->db_fetchSingleRow("
2135 SELECT data as version
2138 name LIKE 'F-Spot Database Version'
2140 return $result['version'];
2144 } // getFspotDBVersion()
2147 * parse the provided URI and will returned the
2150 public function parse_uri($uri, $mode)
2152 if(($components = parse_url($uri)) !== false) {
2156 return basename($components['path']);
2159 return dirname($components['path']);
2162 return $components['path'];