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 for user ". $this->getuid() ."\n";
70 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
71 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
75 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
76 if(!is_writeable($this->cfg->phpfspot_db)) {
77 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
81 $this->check_config_table();
83 /* include Smarty template engine */
84 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
87 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
88 /* overload Smarty class if our own template handler */
89 require_once "phpfspot_tmpl.php";
90 $this->tmpl = new PHPFSPOT_TMPL($this);
92 /* check if all necessary indices exist */
93 $this->checkDbIndices();
97 if(!isset($_SESSION['tag_condition']))
98 $_SESSION['tag_condition'] = 'or';
100 if(!isset($_SESSION['sort_order']))
101 $_SESSION['sort_order'] = 'date_asc';
103 if(!isset($_SESSION['searchfor']))
104 $_SESSION['searchfor'] = '';
106 // if begin_with is still set but rows_per_page is now 0, unset it
107 if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
108 unset($_SESSION['begin_with']);
112 public function __destruct()
118 * show - generate html output
120 * this function can be called after the constructor has
121 * prepared everyhing. it will load the index.tpl smarty
122 * template. if necessary it will registere pre-selects
123 * (photo index, photo, tag search, date search) into
126 public function show()
128 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
129 $this->tmpl->assign('page_title', $this->cfg->page_title);
130 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
131 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
133 if(isset($_GET['mode'])) {
135 $_SESSION['start_action'] = $_GET['mode'];
137 switch($_GET['mode']) {
139 if(isset($_GET['tags'])) {
140 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
142 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
143 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
145 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
146 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
150 if(isset($_GET['tags'])) {
151 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
152 $_SESSION['start_action'] = 'showp';
154 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
155 $_SESSION['current_photo'] = $_GET['id'];
156 $_SESSION['start_action'] = 'showp';
158 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
159 $_SESSION['from_date'] = strtotime($_GET['from_date']);
161 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
162 $_SESSION['to_date'] = strtotime($_GET['to_date']);
166 $this->tmpl->show("export.tpl");
170 $this->tmpl->show("slideshow.tpl");
174 if(isset($_GET['tags'])) {
175 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
177 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
178 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
180 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
181 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
189 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
190 $this->tmpl->assign('date_search_enabled', true);
192 $this->tmpl->assign('from_date', $this->get_calendar('from'));
193 $this->tmpl->assign('to_date', $this->get_calendar('to'));
194 $this->tmpl->assign('sort_field', $this->get_sort_field());
195 $this->tmpl->assign('content_page', 'welcome.tpl');
196 $this->tmpl->show("index.tpl");
201 * get_tags - grab all tags of f-spot's database
203 * this function will get all available tags from
204 * the f-spot database and store them within two
205 * arrays within this class for later usage. in
206 * fact, if the user requests (hide_tags) it will
207 * opt-out some of them.
209 * this function is getting called once by show()
211 private function get_tags()
213 $this->avail_tags = Array();
216 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
219 DISTINCT t1.id as id, t1.name as name
222 INNER JOIN photo_tags
223 pt2 ON pt1.photo_id=pt2.photo_id
229 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
231 t1.sort_priority ASC";
233 $result = $this->db->db_query($query_str);
237 $result = $this->db->db_query("
240 ORDER BY sort_priority ASC
244 while($row = $this->db->db_fetch_object($result)) {
246 $tag_id = $row['id'];
247 $tag_name = $row['name'];
249 /* if the user has specified to ignore this tag in phpfspot's
250 configuration, ignore it here so it does not get added to
253 if(in_array($row['name'], $this->cfg->hide_tags))
256 /* if you include the following if-clause and the user has specified
257 to only show certain tags which are specified in phpfspot's
258 configuration, ignore all others so they will not be added to the
260 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
261 !in_array($row['name'], $this->cfg->show_tags))
265 $this->tags[$tag_id] = $tag_name;
266 $this->avail_tags[$count] = $tag_id;
274 * extract all photo details
276 * retrieve all available details from f-spot's
277 * database and return them as object
279 public function get_photo_details($idx)
281 if($this->dbver < 9) {
283 SELECT p.id, p.name, p.time, p.directory_path, p.description
289 SELECT p.id, p.uri, p.time, p.description
294 /* if show_tags is set, only return details for photos which
295 are specified to be shown
297 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
299 INNER JOIN photo_tags pt
303 WHERE p.id='". $idx ."'
304 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
308 WHERE p.id='". $idx ."'
312 if($result = $this->db->db_query($query_str)) {
314 $row = $this->db->db_fetch_object($result);
316 if($this->dbver < 9) {
317 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
326 } // get_photo_details
329 * returns aligned photo names
331 * this function returns aligned (length) names for
332 * an specific photo. If the length of the name exceeds
333 * $limit the name will be shrinked (...)
335 public function getPhotoName($idx, $limit = 0)
337 if($details = $this->get_photo_details($idx)) {
338 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
339 $name = $this->shrink_text($long_name, $limit);
349 * shrink text according provided limit
351 * If the length of the name exceeds $limit the
352 * text will be shortend and some content in between
353 * will be replaced with "..."
355 private function shrink_text($text, $limit)
357 if($limit != 0 && strlen($text) > $limit) {
358 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
366 * translate f-spoth photo path
368 * as the full-qualified path recorded in the f-spot database
369 * is usally not the same as on the webserver, this function
370 * will replace the path with that one specified in the cfg
372 public function translate_path($path, $width = 0)
374 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
379 * control HTML ouput for a single photo
381 * this function provides all the necessary information
382 * for the single photo template.
384 public function showPhoto($photo)
386 /* get all photos from the current photo selection */
387 $all_photos = $this->getPhotoSelection();
388 $count = count($all_photos);
390 for($i = 0; $i < $count; $i++) {
392 // $get_next will be set, when the photo which has to
393 // be displayed has been found - this means that the
394 // next available is in fact the NEXT image (for the
396 if(isset($get_next)) {
397 $next_img = $all_photos[$i];
401 /* the next photo is our NEXT photo */
402 if($all_photos[$i] == $photo) {
406 $previous_img = $all_photos[$i];
409 if($photo == $all_photos[$i]) {
414 $details = $this->get_photo_details($photo);
421 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
422 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
424 if(!file_exists($orig_path)) {
425 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
428 if(!is_readable($orig_path)) {
429 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
432 /* If the thumbnail doesn't exist yet, try to create it */
433 if(!file_exists($thumb_path)) {
434 $this->gen_thumb($photo, true);
435 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
438 /* get f-spot database meta information */
439 $meta = $this->get_meta_informations($orig_path);
441 /* If EXIF data are available, use them */
442 if(isset($meta['ExifImageWidth'])) {
443 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
445 $info = getimagesize($orig_path);
446 $meta_res = $info[0] ."x". $info[1];
449 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
450 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
451 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
453 $extern_link = "index.php?mode=showp&id=". $photo;
454 $current_tags = $this->getCurrentTags();
455 if($current_tags != "") {
456 $extern_link.= "&tags=". $current_tags;
458 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
459 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
462 $this->tmpl->assign('extern_link', $extern_link);
464 if(file_exists($thumb_path)) {
466 $info = getimagesize($thumb_path);
468 $this->tmpl->assign('description', $details['description']);
469 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
471 $this->tmpl->assign('width', $info[0]);
472 $this->tmpl->assign('height', $info[1]);
473 $this->tmpl->assign('ExifMadeOn', $meta_date);
474 $this->tmpl->assign('ExifMadeWith', $meta_make);
475 $this->tmpl->assign('ExifOrigResolution', $meta_res);
476 $this->tmpl->assign('ExifFileSize', $meta_size);
478 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
479 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
481 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
482 $this->tmpl->assign('current', $current);
485 $this->_error("Can't open file ". $thumb_path ."\n");
490 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
491 $this->tmpl->assign('prev_img', $previous_img);
495 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
496 $this->tmpl->assign('next_img', $next_img);
498 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
499 $this->tmpl->assign('photo_number', $i);
500 $this->tmpl->assign('photo_count', count($all_photos));
502 $this->tmpl->show("single_photo.tpl");
507 * all available tags and tag cloud
509 * this function outputs all available tags (time ordered)
510 * and in addition output them as tag cloud (tags which have
511 * many photos will appears more then others)
513 public function getAvailableTags()
519 $result = $this->db->db_query("
520 SELECT tag_id as id, count(tag_id) as quantity
530 while($row = $this->db->db_fetch_object($result)) {
531 $tags[$row['id']] = $row['quantity'];
534 // change these font sizes if you will
535 $max_size = 125; // max font size in %
536 $min_size = 75; // min font size in %
538 // get the largest and smallest array values
539 $max_qty = max(array_values($tags));
540 $min_qty = min(array_values($tags));
542 // find the range of values
543 $spread = $max_qty - $min_qty;
544 if (0 == $spread) { // we don't want to divide by zero
548 // determine the font-size increment
549 // this is the increase per tag quantity (times used)
550 $step = ($max_size - $min_size)/($spread);
552 // loop through our tag array
553 foreach ($tags as $key => $value) {
555 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
558 // calculate CSS font-size
559 // find the $value in excess of $min_qty
560 // multiply by the font-size increment ($size)
561 // and add the $min_size set above
562 $size = $min_size + (($value - $min_qty) * $step);
563 // uncomment if you want sizes in whole %:
566 if(isset($this->tags[$key])) {
567 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
572 $output = substr($output, 0, strlen($output)-2);
575 } // getAvailableTags()
578 * output all selected tags
580 * this function output all tags which have been selected
581 * by the user. the selected tags are stored in the
582 * session-variable $_SESSION['selected_tags']
584 public function getSelectedTags()
589 foreach($this->avail_tags as $tag)
591 // return all selected tags
592 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
593 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
597 $output = substr($output, 0, strlen($output)-2);
600 } // getSelectedTags()
603 * add tag to users session variable
605 * this function will add the specified to users current
606 * tag selection. if a date search has been made before
607 * it will be now cleared
609 public function addTag($tag)
611 if(!isset($_SESSION['selected_tags']))
612 $_SESSION['selected_tags'] = Array();
614 if(!in_array($tag, $_SESSION['selected_tags']))
615 array_push($_SESSION['selected_tags'], $tag);
620 * remove tag to users session variable
622 * this function removes the specified tag from
623 * users current tag selection
625 public function delTag($tag)
627 if(isset($_SESSION['selected_tags'])) {
628 $key = array_search($tag, $_SESSION['selected_tags']);
629 unset($_SESSION['selected_tags'][$key]);
630 sort($_SESSION['selected_tags']);
636 * reset tag selection
638 * if there is any tag selection, it will be
641 public function resetTags()
643 if(isset($_SESSION['selected_tags']))
644 unset($_SESSION['selected_tags']);
651 * if a specific photo was requested (external link)
652 * unset the session variable now
654 public function resetPhotoView()
656 if(isset($_SESSION['current_photo']))
657 unset($_SESSION['current_photo']);
659 } // resetPhotoView();
664 * if any tag search has taken place, reset
667 public function resetTagSearch()
669 if(isset($_SESSION['searchfor']))
670 unset($_SESSION['searchfor']);
672 } // resetTagSearch()
677 * if any date search has taken place, reset
680 public function resetDateSearch()
682 if(isset($_SESSION['from_date']))
683 unset($_SESSION['from_date']);
684 if(isset($_SESSION['to_date']))
685 unset($_SESSION['to_date']);
687 } // resetDateSearch();
690 * return all photo according selection
692 * this function returns all photos based on
693 * the tag-selection, tag- or date-search.
694 * the tag-search also has to take care of AND
695 * and OR conjunctions
697 public function getPhotoSelection()
699 $matched_photos = Array();
701 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
702 $from_date = $_SESSION['from_date'];
703 $to_date = $_SESSION['to_date'];
704 $additional_where_cond = "
705 p.time>='". $from_date ."'
707 p.time<='". $to_date ."'
711 if(isset($_SESSION['sort_order'])) {
712 $order_str = $this->get_sort_order();
715 /* return a search result */
716 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
718 SELECT DISTINCT pt1.photo_id
720 INNER JOIN photo_tags pt2
721 ON pt1.photo_id=pt2.photo_id
728 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
730 if(isset($additional_where_cond))
731 $query_str.= "AND ". $additional_where_cond ." ";
733 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
734 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
737 if(isset($order_str))
738 $query_str.= $order_str;
740 $result = $this->db->db_query($query_str);
741 while($row = $this->db->db_fetch_object($result)) {
742 array_push($matched_photos, $row['photo_id']);
744 return $matched_photos;
747 /* return according the selected tags */
748 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
750 foreach($_SESSION['selected_tags'] as $tag)
751 $selected.= $tag .",";
752 $selected = substr($selected, 0, strlen($selected)-1);
754 /* photo has to match at least on of the selected tags */
755 if($_SESSION['tag_condition'] == 'or') {
757 SELECT DISTINCT pt1.photo_id
759 INNER JOIN photo_tags pt2
760 ON pt1.photo_id=pt2.photo_id
765 WHERE pt1.tag_id IN (". $selected .")
767 if(isset($additional_where_cond))
768 $query_str.= "AND ". $additional_where_cond ." ";
770 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
771 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
774 if(isset($order_str))
775 $query_str.= $order_str;
777 /* photo has to match all selected tags */
778 elseif($_SESSION['tag_condition'] == 'and') {
780 if(count($_SESSION['selected_tags']) >= 32) {
781 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
782 print "evaluate your tag selection. Please remove some tags from your selection.\n";
786 /* Join together a table looking like
788 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
790 so the query can quickly return all images matching the
791 selected tags in an AND condition
796 SELECT DISTINCT pt1.photo_id
800 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
807 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
809 INNER JOIN photo_tags pt". ($i+2) ."
810 ON pt1.photo_id=pt". ($i+2) .".photo_id
817 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
818 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
820 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
823 if(isset($additional_where_cond))
824 $query_str.= "AND ". $additional_where_cond;
826 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
827 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
830 if(isset($order_str))
831 $query_str.= $order_str;
835 $result = $this->db->db_query($query_str);
836 while($row = $this->db->db_fetch_object($result)) {
837 array_push($matched_photos, $row['photo_id']);
839 return $matched_photos;
842 /* return all available photos */
844 SELECT DISTINCT photo_id
851 if(isset($additional_where_cond))
852 $query_str.= "WHERE ". $additional_where_cond ." ";
854 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
855 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
858 if(isset($order_str))
859 $query_str.= $order_str;
861 $result = $this->db->db_query($query_str);
862 while($row = $this->db->db_fetch_object($result)) {
863 array_push($matched_photos, $row['photo_id']);
865 return $matched_photos;
867 } // getPhotoSelection()
870 * control HTML ouput for photo index
872 * this function provides all the necessary information
873 * for the photo index template.
875 public function showPhotoIndex()
877 $photos = $this->getPhotoSelection();
879 $count = count($photos);
881 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
882 $anchor = $_SESSION['begin_with'];
884 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
890 elseif($this->cfg->rows_per_page > 0) {
892 if(!$_SESSION['begin_with'] || $_SESSION['begin_with'] == 0)
896 $begin_with = $_SESSION['begin_with'];
898 // verify $begin_with - perhaps the thumbs-per-rows or
899 // rows-per-page variables have changed or the jump back
900 // from a photo wasn't exact - so calculate the real new
902 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
903 for($i = 0; $i <= $count; $i+=$multiplicator) {
904 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
911 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
917 $images[$rows] = Array();
918 $img_height[$rows] = Array();
919 $img_width[$rows] = Array();
920 $img_id[$rows] = Array();
921 $img_name[$rows] = Array();
922 $img_title = Array();
924 for($i = $begin_with; $i < $end_with; $i++) {
926 $images[$rows][$cols] = $photos[$i];
927 $img_id[$rows][$cols] = $i;
928 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
929 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
931 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
933 if(file_exists($thumb_path)) {
934 $info = getimagesize($thumb_path);
935 $img_width[$rows][$cols] = $info[0];
936 $img_height[$rows][$cols] = $info[1];
939 if($cols == $this->cfg->thumbs_per_row-1) {
942 $images[$rows] = Array();
943 $img_width[$rows] = Array();
944 $img_height[$rows] = Array();
951 // +1 for for smarty's selection iteration
954 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
955 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
957 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
958 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
959 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
962 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
963 $this->tmpl->assign('tag_result', 1);
966 /* do we have to display the page selector ? */
967 if($this->cfg->rows_per_page != 0) {
969 /* calculate the page switchers */
970 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
971 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
974 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
975 if($end_with < $count)
976 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
978 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
979 $last_page = ceil($count / $photo_per_page);
981 /* get the current selected page */
982 if($begin_with == 0) {
986 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
993 for($i = 1; $i <= $last_page; $i++) {
995 if($current_page == $i)
996 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
997 elseif($current_page-1 == $i || $current_page+1 == $i)
998 $style = "style=\"font-size: 105%;\"";
999 elseif(($current_page-5 >= $i) && ($i != 1) ||
1000 ($current_page+5 <= $i) && ($i != $last_page))
1001 $style = "style=\"font-size: 75%;\"";
1005 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1008 $select.= ">". $i ."</a> ";
1010 // until 9 pages we show the selector from 1-9
1011 if($last_page <= 9) {
1012 $page_select.= $select;
1015 if($i == 1 /* first page */ ||
1016 $i == $last_page /* last page */ ||
1017 $i == $current_page /* current page */ ||
1018 $i == ceil($last_page * 0.25) /* first quater */ ||
1019 $i == ceil($last_page * 0.5) /* half */ ||
1020 $i == ceil($last_page * 0.75) /* third quater */ ||
1021 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1022 (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 */ ||
1023 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1024 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1026 $page_select.= $select;
1034 $page_select.= "......... ";
1039 /* only show the page selector if we have more then one page */
1041 $this->tmpl->assign('page_selector', $page_select);
1045 $current_tags = $this->getCurrentTags();
1046 $extern_link = "index.php?mode=showpi";
1047 $rss_link = "index.php?mode=rss";
1048 if($current_tags != "") {
1049 $extern_link.= "&tags=". $current_tags;
1050 $rss_link.= "&tags=". $current_tags;
1052 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1053 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1054 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1057 $export_link = "index.php?mode=export";
1058 $slideshow_link = "index.php?mode=slideshow";
1060 $this->tmpl->assign('extern_link', $extern_link);
1061 $this->tmpl->assign('slideshow_link', $slideshow_link);
1062 $this->tmpl->assign('export_link', $export_link);
1063 $this->tmpl->assign('rss_link', $rss_link);
1064 $this->tmpl->assign('count', $count);
1065 $this->tmpl->assign('width', $this->cfg->thumb_width);
1066 $this->tmpl->assign('images', $images);
1067 $this->tmpl->assign('img_width', $img_width);
1068 $this->tmpl->assign('img_height', $img_height);
1069 $this->tmpl->assign('img_id', $img_id);
1070 $this->tmpl->assign('img_name', $img_name);
1071 $this->tmpl->assign('img_title', $img_title);
1072 $this->tmpl->assign('rows', $rows);
1073 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1075 $this->tmpl->show("photo_index.tpl");
1078 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1080 } // showPhotoIndex()
1083 * show credit template
1085 public function showCredits()
1087 $this->tmpl->assign('version', $this->cfg->version);
1088 $this->tmpl->assign('product', $this->cfg->product);
1089 $this->tmpl->assign('db_version', $this->dbver);
1090 $this->tmpl->show("credits.tpl");
1095 * create_thumbnails for the requested width
1097 * this function creates image thumbnails of $orig_image
1098 * stored as $thumb_image. It will check if the image is
1099 * in a supported format, if necessary rotate the image
1100 * (based on EXIF orientation meta headers) and re-sizing.
1102 public function create_thumbnail($orig_image, $thumb_image, $width)
1104 if(!file_exists($orig_image)) {
1108 $details = getimagesize($orig_image);
1110 /* check if original photo is a support image type */
1111 if(!$this->checkifImageSupported($details['mime']))
1114 $meta = $this->get_meta_informations($orig_image);
1119 switch($meta['Orientation']) {
1121 case 1: /* top, left */
1122 $rotate = 0; $flip = false; break;
1123 case 2: /* top, right */
1124 $rotate = 0; $flip = true; break;
1125 case 3: /* bottom, left */
1126 $rotate = 180; $flip = false; break;
1127 case 4: /* bottom, right */
1128 $rotate = 180; $flip = true; break;
1129 case 5: /* left side, top */
1130 $rotate = 90; $flip = true; break;
1131 case 6: /* right side, top */
1132 $rotate = 90; $flip = false; break;
1133 case 7: /* left side, bottom */
1134 $rotate = 270; $flip = true; break;
1135 case 8: /* right side, bottom */
1136 $rotate = 270; $flip = false; break;
1139 $src_img = @imagecreatefromjpeg($orig_image);
1142 print "Can't load image from ". $orig_image ."\n";
1146 /* grabs the height and width */
1147 $cur_width = imagesx($src_img);
1148 $cur_height = imagesy($src_img);
1150 // If requested width is more then the actual image width,
1151 // do not generate a thumbnail, instead safe the original
1152 // as thumbnail but with lower quality
1154 if($width >= $cur_width) {
1155 $result = imagejpeg($src_img, $thumb_image, 75);
1156 imagedestroy($src_img);
1160 // If the image will be rotate because EXIF orientation said so
1161 // 'virtually rotate' the image for further calculations
1162 if($rotate == 90 || $rotate == 270) {
1164 $cur_width = $cur_height;
1168 /* calculates aspect ratio */
1169 $aspect_ratio = $cur_height / $cur_width;
1172 if($aspect_ratio < 1) {
1174 $new_h = abs($new_w * $aspect_ratio);
1176 /* 'virtually' rotate the image and calculate it's ratio */
1177 $tmp_w = $cur_height;
1178 $tmp_h = $cur_width;
1179 /* now get the ratio from the 'rotated' image */
1180 $tmp_ratio = $tmp_h/$tmp_w;
1181 /* now calculate the new dimensions */
1183 $tmp_h = abs($tmp_w * $tmp_ratio);
1185 // now that we know, how high they photo should be, if it
1186 // gets rotated, use this high to scale the image
1188 $new_w = abs($new_h / $aspect_ratio);
1190 // If the image will be rotate because EXIF orientation said so
1191 // now 'virtually rotate' back the image for the image manipulation
1192 if($rotate == 90 || $rotate == 270) {
1199 /* creates new image of that size */
1200 $dst_img = imagecreatetruecolor($new_w, $new_h);
1202 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1204 /* copies resized portion of original image into new image */
1205 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1207 /* needs the image to be flipped horizontal? */
1211 for($x = 0; $x < $new_w; $x++) {
1212 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1217 $this->_debug("(ROTATE)");
1218 $dst_img = $this->rotateImage($dst_img, $rotate);
1221 /* write down new generated file */
1222 $result = imagejpeg($dst_img, $thumb_image, 75);
1224 /* free your mind */
1225 imagedestroy($dst_img);
1226 imagedestroy($src_img);
1228 if($result === false) {
1229 print "Can't write thumbnail ". $thumb_image ."\n";
1235 } // create_thumbnail()
1238 * return all exif meta data from the file
1240 public function get_meta_informations($file)
1242 return exif_read_data($file);
1244 } // get_meta_informations()
1247 * create phpfspot own sqlite database
1249 * this function creates phpfspots own sqlite database
1250 * if it does not exist yet. this own is used to store
1251 * some necessary informations (md5 sum's, ...).
1253 public function check_config_table()
1255 // if the config table doesn't exist yet, create it
1256 if(!$this->cfg_db->db_check_table_exists("images")) {
1257 $this->cfg_db->db_exec("
1258 CREATE TABLE images (
1259 img_idx int primary key,
1265 } // check_config_table
1268 * Generates a thumbnail from photo idx
1270 * This function will generate JPEG thumbnails from provided F-Spot photo
1273 * 1. Check if all thumbnail generations (width) are already in place and
1275 * 2. Check if the md5sum of the original file has changed
1276 * 3. Generate the thumbnails if needed
1278 public function gen_thumb($idx = 0, $force = 0)
1282 $resolutions = Array(
1283 $this->cfg->thumb_width,
1284 $this->cfg->photo_width,
1285 $this->cfg->mini_width,
1288 /* get details from F-Spot's database */
1289 $details = $this->get_photo_details($idx);
1291 /* calculate file MD5 sum */
1292 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1294 if(!file_exists($full_path)) {
1295 $this->_error("File ". $full_path ." does not exist\n");
1299 if(!is_readable($full_path)) {
1300 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1304 $file_md5 = md5_file($full_path);
1306 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1310 foreach($resolutions as $resolution) {
1312 $thumb_sub_path = substr($file_md5, 0, 2);
1313 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1315 if(!file_exists(dirname($thumb_path))) {
1316 mkdir(dirname($thumb_path), 0755);
1319 /* if the thumbnail file doesn't exist, create it */
1320 if(!file_exists($thumb_path)) {
1322 $this->_debug(" ". $resolution ."px");
1323 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1328 /* if the file hasn't changed there is no need to regen the thumb */
1329 elseif($file_md5 != $this->getMD5($idx) || $force) {
1331 $this->_debug(" ". $resolution ."px");
1332 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1340 $this->_debug(" already exist");
1343 /* set the new/changed MD5 sum for the current photo */
1345 $this->setMD5($idx, $file_md5);
1348 $this->_debug("\n");
1353 * returns stored md5 sum for a specific photo
1355 * this function queries the phpfspot database for a
1356 * stored MD5 checksum of the specified photo
1358 public function getMD5($idx)
1360 $result = $this->cfg_db->db_query("
1363 WHERE img_idx='". $idx ."'
1369 $img = $this->cfg_db->db_fetch_object($result);
1370 return $img['img_md5'];
1375 * set MD5 sum for the specific photo
1377 private function setMD5($idx, $md5)
1379 $result = $this->cfg_db->db_exec("
1380 REPLACE INTO images (img_idx, img_md5)
1381 VALUES ('". $idx ."', '". $md5 ."')
1387 * store current tag condition
1389 * this function stores the current tag condition
1390 * (AND or OR) in the users session variables
1392 public function setTagCondition($mode)
1394 $_SESSION['tag_condition'] = $mode;
1396 } // setTagCondition()
1399 * invoke tag & date search
1401 * this function will return all matching tags and store
1402 * them in the session variable selected_tags. furthermore
1403 * it also handles the date search.
1404 * getPhotoSelection() will then only return the matching
1407 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1411 $_SESSION['searchfor'] = $searchfor;
1412 $_SESSION['sort_order'] = $sort_order;
1414 $_SESSION['from_date'] = strtotime($from);
1416 unset($_SESSION['from_date']);
1418 $_SESSION['to_date'] = strtotime($to);
1420 unset($_SESSION['to_date']);
1422 if($searchfor != "") {
1423 /* new search, reset the current selected tags */
1424 $_SESSION['selected_tags'] = Array();
1425 foreach($this->avail_tags as $tag) {
1426 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1427 array_push($_SESSION['selected_tags'], $tag);
1436 * this function rotates the image according the
1439 private function rotateImage($img, $degrees)
1441 if(function_exists("imagerotate")) {
1442 $img = imagerotate($img, $degrees, 0);
1444 function imagerotate($src_img, $angle)
1446 $src_x = imagesx($src_img);
1447 $src_y = imagesy($src_img);
1448 if ($angle == 180) {
1452 elseif ($src_x <= $src_y) {
1456 elseif ($src_x >= $src_y) {
1461 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1462 imagealphablending($rotate, false);
1467 for ($y = 0; $y < ($src_y); $y++) {
1468 for ($x = 0; $x < ($src_x); $x++) {
1469 $color = imagecolorat($src_img, $x, $y);
1470 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1476 for ($y = 0; $y < ($src_y); $y++) {
1477 for ($x = 0; $x < ($src_x); $x++) {
1478 $color = imagecolorat($src_img, $x, $y);
1479 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1485 for ($y = 0; $y < ($src_y); $y++) {
1486 for ($x = 0; $x < ($src_x); $x++) {
1487 $color = imagecolorat($src_img, $x, $y);
1488 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1502 $img = imagerotate($img, $degrees);
1511 * return all assigned tags for the specified photo
1513 private function get_photo_tags($idx)
1515 $result = $this->db->db_query("
1518 INNER JOIN photo_tags pt
1520 WHERE pt.photo_id='". $idx ."'
1525 while($row = $this->db->db_fetch_object($result))
1526 $tags[$row['id']] = $row['name'];
1530 } // get_photo_tags()
1533 * create on-the-fly images with text within
1535 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1537 if (strlen($color) != 6)
1540 $int = hexdec($color);
1541 $h = imagefontheight($font);
1542 $fw = imagefontwidth($font);
1543 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1544 $lines = count($txt);
1545 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1546 $bg = imagecolorallocate($im, 255, 255, 255);
1547 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1550 foreach ($txt as $text) {
1551 $x = (($w - ($fw * strlen($text))) / 2);
1552 imagestring($im, $font, $x, $y, $text, $color);
1553 $y += ($h + $space);
1556 Header("Content-type: image/png");
1559 } // showTextImage()
1562 * check if all requirements are met
1564 private function checkRequirements()
1566 if(!function_exists("imagecreatefromjpeg")) {
1567 print "PHP GD library extension is missing<br />\n";
1571 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1572 print "PHP SQLite3 library extension is missing<br />\n";
1576 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1577 ini_set('track_errors', 1);
1578 @include_once 'HTML/AJAX/Server.php';
1579 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1580 print "PEAR HTML_AJAX package is missing<br />\n";
1583 @include_once 'Calendar/Calendar.php';
1584 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1585 print "PEAR Calendar package is missing<br />\n";
1588 ini_restore('track_errors');
1595 } // checkRequirements()
1597 private function _debug($text)
1599 if($this->fromcmd) {
1606 * check if specified MIME type is supported
1608 public function checkifImageSupported($mime)
1610 if(in_array($mime, Array("image/jpeg")))
1615 } // checkifImageSupported()
1617 public function _error($text)
1619 switch($this->cfg->logging) {
1621 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1628 error_log($text, 3, $his->cfg->log_file);
1635 * output calendard input fields
1637 private function get_calendar($mode)
1639 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1640 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1641 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1643 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1644 if(!isset($_SESSION[$mode .'_date']))
1645 $output.= " disabled=\"disabled\"";
1647 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1648 if(!isset($_SESSION[$mode .'_date']))
1649 $output.= " disabled=\"disabled\"";
1651 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1652 if(!isset($_SESSION[$mode .'_date']))
1653 $output.= " disabled=\"disabled\"";
1661 * output calendar matrix
1663 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1665 if (!isset($year)) $year = date('Y');
1666 if (!isset($month)) $month = date('m');
1667 if (!isset($day)) $day = date('d');
1672 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1673 require_once CALENDAR_ROOT.'Day.php';
1676 $month = new Calendar_Month_Weekdays($year,$month);
1679 $prevStamp = $month->prevMonth(true);
1680 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1681 $nextStamp = $month->nextMonth(true);
1682 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1684 $selectedDays = array (
1685 new Calendar_Day($year,$month,$day),
1686 new Calendar_Day($year,12,25),
1689 // Build the days in the month
1690 $month->build($selectedDays);
1692 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1693 $this->tmpl->assign('prev_month', $prev);
1694 $this->tmpl->assign('next_month', $next);
1696 while ( $day = $month->fetch() ) {
1698 if(!isset($matrix[$rows]))
1699 $matrix[$rows] = Array();
1703 $dayStamp = $day->thisDay(true);
1704 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1706 // isFirst() to find start of week
1707 if ( $day->isFirst() )
1710 if ( $day->isSelected() ) {
1711 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1712 } else if ( $day->isEmpty() ) {
1713 $string.= "<td> </td>\n";
1715 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1718 // isLast() to find end of week
1719 if ( $day->isLast() )
1720 $string.= "</tr>\n";
1722 $matrix[$rows][$cols] = $string;
1732 $this->tmpl->assign('matrix', $matrix);
1733 $this->tmpl->assign('rows', $rows);
1734 $this->tmpl->show("calendar.tpl");
1736 } // get_calendar_matrix()
1739 * output export page
1741 public function getExport($mode)
1743 $pictures = $this->getPhotoSelection();
1744 $current_tags = $this->getCurrentTags();
1746 foreach($pictures as $picture) {
1748 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1749 if($current_tags != "") {
1750 $orig_url.= "&tags=". $current_tags;
1752 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1753 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1756 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1761 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1762 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1766 // "[%pictureurl% %thumbnailurl%]"
1767 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1770 case 'MoinMoinList':
1771 // " * [%pictureurl% %thumbnailurl%]"
1772 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1783 public function getRSSFeed()
1785 Header("Content-type: text/xml; charset=utf-8");
1786 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1789 xmlns:media="http://search.yahoo.com/mrss/"
1790 xmlns:dc="http://purl.org/dc/elements/1.1/"
1793 <title>phpfspot</title>
1794 <description>phpfspot RSS feed</description>
1795 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1796 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1797 <generator>phpfspot</generator>
1800 $pictures = $this->getPhotoSelection();
1801 $current_tags = $this->getCurrentTags();
1803 foreach($pictures as $picture) {
1805 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1806 if($current_tags != "") {
1807 $orig_url.= "&tags=". $current_tags;
1809 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1810 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1813 $details = $this->get_photo_details($picture);
1815 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1816 $thumb_html = htmlspecialchars("
1817 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1819 ". $details['description']);
1821 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1822 $meta = $this->get_meta_informations($orig_path);
1823 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1827 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1828 <link><?php print htmlspecialchars($orig_url); ?></link>
1829 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1830 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1832 <?php print $thumb_html; ?>
1834 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1849 * return all selected tags as one string
1851 private function getCurrentTags()
1854 if($_SESSION['selected_tags'] != "") {
1855 foreach($_SESSION['selected_tags'] as $tag)
1856 $current_tags.= $tag .",";
1857 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1859 return $current_tags;
1861 } // getCurrentTags()
1864 * return the current photo
1866 public function getCurrentPhoto()
1868 if(isset($_SESSION['current_photo'])) {
1869 print $_SESSION['current_photo'];
1871 } // getCurrentPhoto()
1874 * tells the client browser what to do
1876 * this function is getting called via AJAX by the
1877 * client browsers. it will tell them what they have
1878 * to do next. This is necessary for directly jumping
1879 * into photo index or single photo view when the are
1880 * requested with specific URLs
1882 public function whatToDo()
1884 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1885 return "show_photo";
1887 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1888 return "showpi_tags";
1890 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1894 return "nothing special";
1899 * return the current process-user
1901 private function getuid()
1903 if($uid = posix_getuid()) {
1904 if($user = posix_getpwuid($uid)) {
1905 return $user['name'];
1914 * returns a select-dropdown box to select photo index sort parameters
1916 private function get_sort_field()
1918 $output = "<select name=\"sort_order\">";
1919 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1920 $output.= "<option value=\"". $sort_order ."\"";
1921 if($sort_order == $_SESSION['sort_order']) {
1922 $output.= " selected=\"selected\"";
1924 $output.= ">". $sort_order ."</option>";
1926 $output.= "</select>";
1929 } // get_sort_field()
1932 * returns the currently selected sort order
1934 private function get_sort_order()
1936 switch($_SESSION['sort_order']) {
1938 return " ORDER BY p.time ASC";
1941 return " ORDER BY p.time DESC";
1944 return " ORDER BY p.name ASC";
1947 return " ORDER BY p.name DESC";
1951 } // get_sort_order()
1954 * return the next to be shown slide show image
1956 * this function returns the URL of the next image
1957 * in the slideshow sequence.
1959 public function getNextSlideShowImage()
1961 $all_photos = $this->getPhotoSelection();
1963 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
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 } // getNextSlideShowImage()
1973 * return the previous to be shown slide show image
1975 * this function returns the URL of the previous image
1976 * in the slideshow sequence.
1978 public function getPrevSlideShowImage()
1980 $all_photos = $this->getPhotoSelection();
1982 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1983 $_SESSION['slideshow_img'] = 0;
1985 $_SESSION['slideshow_img']--;
1987 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1989 } // getPrevSlideShowImage()
1991 public function resetSlideShow()
1993 if(isset($_SESSION['slideshow_img']))
1994 unset($_SESSION['slideshow_img']);
1995 } // resetSlideShow()
2000 * this function will get all photos from the fspot
2001 * database and randomly return ONE entry
2003 * saddly there is yet no sqlite3 function which returns
2004 * the bulk result in array, so we have to fill up our
2007 public function get_random_photo()
2011 $result = $this->db->db_query("
2016 while($row = $this->db->db_fetch_object($result)) {
2017 array_push($all, $row['id']);
2020 return $all[array_rand($all)];
2022 } // get_random_photo()
2025 * validates provided date
2027 * this function validates if the provided date
2028 * contains a valid date and will return true
2031 public function isValidDate($date_str)
2033 $timestamp = strtotime($date_str);
2035 if(is_numeric($timestamp))
2043 * timestamp to string conversion
2045 private function ts2str($timestamp)
2047 return strftime("%Y-%m-%d", $timestamp);
2050 private function extractTags($tags_str)
2052 $not_validated = split(',', $_GET['tags']);
2053 $validated = array();
2055 foreach($not_validated as $tag) {
2056 if(is_numeric($tag))
2057 array_push($validated, $tag);
2065 * returns the full path to a thumbnail
2067 public function get_thumb_path($width, $photo)
2069 $md5 = $this->getMD5($photo);
2070 $sub_path = substr($md5, 0, 2);
2071 return $this->cfg->thumb_path
2079 } // get_thumb_path()
2082 * returns server's virtual host name
2084 private function get_server_name()
2086 return $_SERVER['SERVER_NAME'];
2087 } // get_server_name()
2090 * returns type of webprotocol which is
2093 private function get_web_protocol()
2095 if(!isset($_SERVER['HTTPS']))
2099 } // get_web_protocol()
2102 * return url to this phpfspot installation
2104 private function get_phpfspot_url()
2106 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2107 } // get_phpfspot_url()
2110 * check file exists and is readable
2112 * returns true, if everything is ok, otherwise false
2113 * if $silent is not set, this function will output and
2116 private function check_readable($file, $silent = null)
2118 if(!file_exists($file)) {
2120 print "File \"". $file ."\" does not exist.\n";
2124 if(!is_readable($file)) {
2126 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2132 } // check_readable()
2135 * check if all needed indices are present
2137 * this function checks, if some needed indices are already
2138 * present, or if not, create them on the fly. they are
2139 * necessary to speed up some queries like that one look for
2140 * all tags, when show_tags is specified in the configuration.
2142 private function checkDbIndices()
2144 $result = $this->db->db_exec("
2145 CREATE INDEX IF NOT EXISTS
2152 } // checkDbIndices()
2155 * retrive F-Spot database version
2157 * this function will return the F-Spot database version number
2158 * It is stored within the sqlite3 database in the table meta
2160 public function getFspotDBVersion()
2162 if($result = $this->db->db_fetchSingleRow("
2163 SELECT data as version
2166 name LIKE 'F-Spot Database Version'
2168 return $result['version'];
2172 } // getFspotDBVersion()
2175 * parse the provided URI and will returned the
2178 public function parse_uri($uri, $mode)
2180 if(($components = parse_url($uri)) !== false) {
2184 return basename($components['path']);
2187 return dirname($components['path']);
2190 return $components['path'];