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();
95 /* if session is not yet started, do it now */
96 if(session_id() == "")
99 if(!isset($_SESSION['tag_condition']))
100 $_SESSION['tag_condition'] = 'or';
102 if(!isset($_SESSION['sort_order']))
103 $_SESSION['sort_order'] = 'date_asc';
105 if(!isset($_SESSION['searchfor']))
106 $_SESSION['searchfor'] = '';
108 // if begin_with is still set but rows_per_page is now 0, unset it
109 if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
110 unset($_SESSION['begin_with']);
114 public function __destruct()
120 * show - generate html output
122 * this function can be called after the constructor has
123 * prepared everyhing. it will load the index.tpl smarty
124 * template. if necessary it will registere pre-selects
125 * (photo index, photo, tag search, date search) into
128 public function show()
130 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
131 $this->tmpl->assign('page_title', $this->cfg->page_title);
132 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
133 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
135 if(isset($_GET['mode'])) {
137 $_SESSION['start_action'] = $_GET['mode'];
139 switch($_GET['mode']) {
141 if(isset($_GET['tags'])) {
142 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
144 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
145 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
147 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
148 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
152 if(isset($_GET['tags'])) {
153 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
154 $_SESSION['start_action'] = 'showp';
156 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
157 $_SESSION['current_photo'] = $_GET['id'];
158 $_SESSION['start_action'] = 'showp';
160 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
161 $_SESSION['from_date'] = strtotime($_GET['from_date']);
163 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
164 $_SESSION['to_date'] = strtotime($_GET['to_date']);
168 $this->tmpl->show("export.tpl");
172 $this->tmpl->show("slideshow.tpl");
176 if(isset($_GET['tags'])) {
177 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
179 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
180 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
182 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
183 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
191 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
192 $this->tmpl->assign('date_search_enabled', true);
194 $this->tmpl->assign('from_date', $this->get_calendar('from'));
195 $this->tmpl->assign('to_date', $this->get_calendar('to'));
196 $this->tmpl->assign('sort_field', $this->get_sort_field());
197 $this->tmpl->assign('content_page', 'welcome.tpl');
198 $this->tmpl->show("index.tpl");
203 * get_tags - grab all tags of f-spot's database
205 * this function will get all available tags from
206 * the f-spot database and store them within two
207 * arrays within this class for later usage. in
208 * fact, if the user requests (hide_tags) it will
209 * opt-out some of them.
211 * this function is getting called once by show()
213 private function get_tags()
215 $this->avail_tags = Array();
218 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
221 DISTINCT t1.id as id, t1.name as name
224 INNER JOIN photo_tags
225 pt2 ON pt1.photo_id=pt2.photo_id
231 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
233 t1.sort_priority ASC";
235 $result = $this->db->db_query($query_str);
239 $result = $this->db->db_query("
242 ORDER BY sort_priority ASC
246 while($row = $this->db->db_fetch_object($result)) {
248 $tag_id = $row['id'];
249 $tag_name = $row['name'];
251 /* if the user has specified to ignore this tag in phpfspot's
252 configuration, ignore it here so it does not get added to
255 if(in_array($row['name'], $this->cfg->hide_tags))
258 /* if you include the following if-clause and the user has specified
259 to only show certain tags which are specified in phpfspot's
260 configuration, ignore all others so they will not be added to the
262 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
263 !in_array($row['name'], $this->cfg->show_tags))
267 $this->tags[$tag_id] = $tag_name;
268 $this->avail_tags[$count] = $tag_id;
276 * extract all photo details
278 * retrieve all available details from f-spot's
279 * database and return them as object
281 public function get_photo_details($idx)
283 if($this->dbver < 9) {
285 SELECT p.id, p.name, p.time, p.directory_path, p.description
291 SELECT p.id, p.uri, p.time, p.description
296 /* if show_tags is set, only return details for photos which
297 are specified to be shown
299 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
301 INNER JOIN photo_tags pt
305 WHERE p.id='". $idx ."'
306 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
310 WHERE p.id='". $idx ."'
314 if($result = $this->db->db_query($query_str)) {
316 $row = $this->db->db_fetch_object($result);
318 if($this->dbver < 9) {
319 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
328 } // get_photo_details
331 * returns aligned photo names
333 * this function returns aligned (length) names for
334 * an specific photo. If the length of the name exceeds
335 * $limit the name will be shrinked (...)
337 public function getPhotoName($idx, $limit = 0)
339 if($details = $this->get_photo_details($idx)) {
340 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
341 $name = $this->shrink_text($long_name, $limit);
351 * shrink text according provided limit
353 * If the length of the name exceeds $limit the
354 * text will be shortend and some content in between
355 * will be replaced with "..."
357 private function shrink_text($text, $limit)
359 if($limit != 0 && strlen($text) > $limit) {
360 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
368 * translate f-spoth photo path
370 * as the full-qualified path recorded in the f-spot database
371 * is usally not the same as on the webserver, this function
372 * will replace the path with that one specified in the cfg
374 public function translate_path($path, $width = 0)
376 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
381 * control HTML ouput for a single photo
383 * this function provides all the necessary information
384 * for the single photo template.
386 public function showPhoto($photo)
388 /* get all photos from the current photo selection */
389 $all_photos = $this->getPhotoSelection();
390 $count = count($all_photos);
392 for($i = 0; $i < $count; $i++) {
394 // $get_next will be set, when the photo which has to
395 // be displayed has been found - this means that the
396 // next available is in fact the NEXT image (for the
398 if(isset($get_next)) {
399 $next_img = $all_photos[$i];
403 /* the next photo is our NEXT photo */
404 if($all_photos[$i] == $photo) {
408 $previous_img = $all_photos[$i];
411 if($photo == $all_photos[$i]) {
416 $details = $this->get_photo_details($photo);
423 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
424 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
426 if(!file_exists($orig_path)) {
427 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
430 if(!is_readable($orig_path)) {
431 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
434 /* If the thumbnail doesn't exist yet, try to create it */
435 if(!file_exists($thumb_path)) {
436 $this->gen_thumb($photo, true);
437 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
440 /* get f-spot database meta information */
441 $meta = $this->get_meta_informations($orig_path);
443 /* If EXIF data are available, use them */
444 if(isset($meta['ExifImageWidth'])) {
445 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
447 $info = getimagesize($orig_path);
448 $meta_res = $info[0] ."x". $info[1];
451 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
452 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
453 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
455 $extern_link = "index.php?mode=showp&id=". $photo;
456 $current_tags = $this->getCurrentTags();
457 if($current_tags != "") {
458 $extern_link.= "&tags=". $current_tags;
460 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
461 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
464 $this->tmpl->assign('extern_link', $extern_link);
466 if(file_exists($thumb_path)) {
468 $info = getimagesize($thumb_path);
470 $this->tmpl->assign('description', $details['description']);
471 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
473 $this->tmpl->assign('width', $info[0]);
474 $this->tmpl->assign('height', $info[1]);
475 $this->tmpl->assign('ExifMadeOn', $meta_date);
476 $this->tmpl->assign('ExifMadeWith', $meta_make);
477 $this->tmpl->assign('ExifOrigResolution', $meta_res);
478 $this->tmpl->assign('ExifFileSize', $meta_size);
480 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
481 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
483 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
484 $this->tmpl->assign('current', $current);
487 $this->_error("Can't open file ". $thumb_path ."\n");
492 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
493 $this->tmpl->assign('prev_img', $previous_img);
497 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
498 $this->tmpl->assign('next_img', $next_img);
500 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
501 $this->tmpl->assign('photo_number', $i);
502 $this->tmpl->assign('photo_count', count($all_photos));
504 $this->tmpl->show("single_photo.tpl");
509 * all available tags and tag cloud
511 * this function outputs all available tags (time ordered)
512 * and in addition output them as tag cloud (tags which have
513 * many photos will appears more then others)
515 public function getAvailableTags()
521 $result = $this->db->db_query("
522 SELECT tag_id as id, count(tag_id) as quantity
532 while($row = $this->db->db_fetch_object($result)) {
533 $tags[$row['id']] = $row['quantity'];
536 // change these font sizes if you will
537 $max_size = 125; // max font size in %
538 $min_size = 75; // min font size in %
540 // get the largest and smallest array values
541 $max_qty = max(array_values($tags));
542 $min_qty = min(array_values($tags));
544 // find the range of values
545 $spread = $max_qty - $min_qty;
546 if (0 == $spread) { // we don't want to divide by zero
550 // determine the font-size increment
551 // this is the increase per tag quantity (times used)
552 $step = ($max_size - $min_size)/($spread);
554 // loop through our tag array
555 foreach ($tags as $key => $value) {
557 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
560 // calculate CSS font-size
561 // find the $value in excess of $min_qty
562 // multiply by the font-size increment ($size)
563 // and add the $min_size set above
564 $size = $min_size + (($value - $min_qty) * $step);
565 // uncomment if you want sizes in whole %:
568 if(isset($this->tags[$key])) {
569 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
574 $output = substr($output, 0, strlen($output)-2);
577 } // getAvailableTags()
580 * output all selected tags
582 * this function output all tags which have been selected
583 * by the user. the selected tags are stored in the
584 * session-variable $_SESSION['selected_tags']
586 public function getSelectedTags()
591 foreach($this->avail_tags as $tag)
593 // return all selected tags
594 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
595 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
599 $output = substr($output, 0, strlen($output)-2);
602 } // getSelectedTags()
605 * add tag to users session variable
607 * this function will add the specified to users current
608 * tag selection. if a date search has been made before
609 * it will be now cleared
611 public function addTag($tag)
613 if(!isset($_SESSION['selected_tags']))
614 $_SESSION['selected_tags'] = Array();
616 if(!in_array($tag, $_SESSION['selected_tags']))
617 array_push($_SESSION['selected_tags'], $tag);
622 * remove tag to users session variable
624 * this function removes the specified tag from
625 * users current tag selection
627 public function delTag($tag)
629 if(isset($_SESSION['selected_tags'])) {
630 $key = array_search($tag, $_SESSION['selected_tags']);
631 unset($_SESSION['selected_tags'][$key]);
632 sort($_SESSION['selected_tags']);
638 * reset tag selection
640 * if there is any tag selection, it will be
643 public function resetTags()
645 if(isset($_SESSION['selected_tags']))
646 unset($_SESSION['selected_tags']);
653 * if a specific photo was requested (external link)
654 * unset the session variable now
656 public function resetPhotoView()
658 if(isset($_SESSION['current_photo']))
659 unset($_SESSION['current_photo']);
661 } // resetPhotoView();
666 * if any tag search has taken place, reset
669 public function resetTagSearch()
671 if(isset($_SESSION['searchfor']))
672 unset($_SESSION['searchfor']);
674 } // resetTagSearch()
679 * if any date search has taken place, reset
682 public function resetDateSearch()
684 if(isset($_SESSION['from_date']))
685 unset($_SESSION['from_date']);
686 if(isset($_SESSION['to_date']))
687 unset($_SESSION['to_date']);
689 } // resetDateSearch();
692 * return all photo according selection
694 * this function returns all photos based on
695 * the tag-selection, tag- or date-search.
696 * the tag-search also has to take care of AND
697 * and OR conjunctions
699 public function getPhotoSelection()
701 $matched_photos = Array();
703 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
704 $from_date = $_SESSION['from_date'];
705 $to_date = $_SESSION['to_date'];
706 $additional_where_cond = "
707 p.time>='". $from_date ."'
709 p.time<='". $to_date ."'
713 if(isset($_SESSION['sort_order'])) {
714 $order_str = $this->get_sort_order();
717 /* return a search result */
718 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
720 SELECT DISTINCT pt1.photo_id
722 INNER JOIN photo_tags pt2
723 ON pt1.photo_id=pt2.photo_id
730 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
732 if(isset($additional_where_cond))
733 $query_str.= "AND ". $additional_where_cond ." ";
735 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
736 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
739 if(isset($order_str))
740 $query_str.= $order_str;
742 $result = $this->db->db_query($query_str);
743 while($row = $this->db->db_fetch_object($result)) {
744 array_push($matched_photos, $row['photo_id']);
746 return $matched_photos;
749 /* return according the selected tags */
750 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
752 foreach($_SESSION['selected_tags'] as $tag)
753 $selected.= $tag .",";
754 $selected = substr($selected, 0, strlen($selected)-1);
756 /* photo has to match at least on of the selected tags */
757 if($_SESSION['tag_condition'] == 'or') {
759 SELECT DISTINCT pt1.photo_id
761 INNER JOIN photo_tags pt2
762 ON pt1.photo_id=pt2.photo_id
767 WHERE pt1.tag_id IN (". $selected .")
769 if(isset($additional_where_cond))
770 $query_str.= "AND ". $additional_where_cond ." ";
772 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
773 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
776 if(isset($order_str))
777 $query_str.= $order_str;
779 /* photo has to match all selected tags */
780 elseif($_SESSION['tag_condition'] == 'and') {
782 if(count($_SESSION['selected_tags']) >= 32) {
783 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
784 print "evaluate your tag selection. Please remove some tags from your selection.\n";
788 /* Join together a table looking like
790 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
792 so the query can quickly return all images matching the
793 selected tags in an AND condition
798 SELECT DISTINCT pt1.photo_id
802 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
809 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
811 INNER JOIN photo_tags pt". ($i+2) ."
812 ON pt1.photo_id=pt". ($i+2) .".photo_id
819 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
820 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
822 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
825 if(isset($additional_where_cond))
826 $query_str.= "AND ". $additional_where_cond;
828 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
829 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
832 if(isset($order_str))
833 $query_str.= $order_str;
837 $result = $this->db->db_query($query_str);
838 while($row = $this->db->db_fetch_object($result)) {
839 array_push($matched_photos, $row['photo_id']);
841 return $matched_photos;
844 /* return all available photos */
846 SELECT DISTINCT photo_id
853 if(isset($additional_where_cond))
854 $query_str.= "WHERE ". $additional_where_cond ." ";
856 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
857 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
860 if(isset($order_str))
861 $query_str.= $order_str;
863 $result = $this->db->db_query($query_str);
864 while($row = $this->db->db_fetch_object($result)) {
865 array_push($matched_photos, $row['photo_id']);
867 return $matched_photos;
869 } // getPhotoSelection()
872 * control HTML ouput for photo index
874 * this function provides all the necessary information
875 * for the photo index template.
877 public function showPhotoIndex()
879 $photos = $this->getPhotoSelection();
881 $count = count($photos);
883 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
884 $anchor = $_SESSION['begin_with'];
886 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
892 elseif($this->cfg->rows_per_page > 0) {
894 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
899 $begin_with = $_SESSION['begin_with'];
901 // verify $begin_with - perhaps the thumbs-per-rows or
902 // rows-per-page variables have changed or the jump back
903 // from a photo wasn't exact - so calculate the real new
905 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
906 for($i = 0; $i <= $count; $i+=$multiplicator) {
907 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
914 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
920 $images[$rows] = Array();
921 $img_height[$rows] = Array();
922 $img_width[$rows] = Array();
923 $img_id[$rows] = Array();
924 $img_name[$rows] = Array();
925 $img_title = Array();
927 for($i = $begin_with; $i < $end_with; $i++) {
929 if(isset($photos[$i])) {
931 $images[$rows][$cols] = $photos[$i];
932 $img_id[$rows][$cols] = $i;
933 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
934 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
936 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
938 if(file_exists($thumb_path)) {
939 $info = getimagesize($thumb_path);
940 $img_width[$rows][$cols] = $info[0];
941 $img_height[$rows][$cols] = $info[1];
944 if($cols == $this->cfg->thumbs_per_row-1) {
947 $images[$rows] = Array();
948 $img_width[$rows] = Array();
949 $img_height[$rows] = Array();
957 // +1 for for smarty's selection iteration
960 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
961 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
963 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
964 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
965 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
968 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
969 $this->tmpl->assign('tag_result', 1);
972 /* do we have to display the page selector ? */
973 if($this->cfg->rows_per_page != 0) {
977 /* calculate the page switchers */
978 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
979 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
982 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
983 if($end_with < $count)
984 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
986 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
987 $last_page = ceil($count / $photo_per_page);
989 /* get the current selected page */
990 if($begin_with == 0) {
994 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1001 for($i = 1; $i <= $last_page; $i++) {
1003 if($current_page == $i)
1004 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1005 elseif($current_page-1 == $i || $current_page+1 == $i)
1006 $style = "style=\"font-size: 105%;\"";
1007 elseif(($current_page-5 >= $i) && ($i != 1) ||
1008 ($current_page+5 <= $i) && ($i != $last_page))
1009 $style = "style=\"font-size: 75%;\"";
1013 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1016 $select.= ">". $i ."</a> ";
1018 // until 9 pages we show the selector from 1-9
1019 if($last_page <= 9) {
1020 $page_select.= $select;
1023 if($i == 1 /* first page */ ||
1024 $i == $last_page /* last page */ ||
1025 $i == $current_page /* current page */ ||
1026 $i == ceil($last_page * 0.25) /* first quater */ ||
1027 $i == ceil($last_page * 0.5) /* half */ ||
1028 $i == ceil($last_page * 0.75) /* third quater */ ||
1029 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1030 (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 */ ||
1031 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1032 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1034 $page_select.= $select;
1042 $page_select.= "......... ";
1047 /* only show the page selector if we have more then one page */
1049 $this->tmpl->assign('page_selector', $page_select);
1053 $current_tags = $this->getCurrentTags();
1054 $extern_link = "index.php?mode=showpi";
1055 $rss_link = "index.php?mode=rss";
1056 if($current_tags != "") {
1057 $extern_link.= "&tags=". $current_tags;
1058 $rss_link.= "&tags=". $current_tags;
1060 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1061 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1062 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1065 $export_link = "index.php?mode=export";
1066 $slideshow_link = "index.php?mode=slideshow";
1068 $this->tmpl->assign('extern_link', $extern_link);
1069 $this->tmpl->assign('slideshow_link', $slideshow_link);
1070 $this->tmpl->assign('export_link', $export_link);
1071 $this->tmpl->assign('rss_link', $rss_link);
1072 $this->tmpl->assign('count', $count);
1073 $this->tmpl->assign('width', $this->cfg->thumb_width);
1074 $this->tmpl->assign('images', $images);
1075 $this->tmpl->assign('img_width', $img_width);
1076 $this->tmpl->assign('img_height', $img_height);
1077 $this->tmpl->assign('img_id', $img_id);
1078 $this->tmpl->assign('img_name', $img_name);
1079 $this->tmpl->assign('img_title', $img_title);
1080 $this->tmpl->assign('rows', $rows);
1081 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1083 $this->tmpl->show("photo_index.tpl");
1086 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1088 } // showPhotoIndex()
1091 * show credit template
1093 public function showCredits()
1095 $this->tmpl->assign('version', $this->cfg->version);
1096 $this->tmpl->assign('product', $this->cfg->product);
1097 $this->tmpl->assign('db_version', $this->dbver);
1098 $this->tmpl->show("credits.tpl");
1103 * create_thumbnails for the requested width
1105 * this function creates image thumbnails of $orig_image
1106 * stored as $thumb_image. It will check if the image is
1107 * in a supported format, if necessary rotate the image
1108 * (based on EXIF orientation meta headers) and re-sizing.
1110 public function create_thumbnail($orig_image, $thumb_image, $width)
1112 if(!file_exists($orig_image)) {
1116 $details = getimagesize($orig_image);
1118 /* check if original photo is a support image type */
1119 if(!$this->checkifImageSupported($details['mime']))
1122 $meta = $this->get_meta_informations($orig_image);
1127 switch($meta['Orientation']) {
1129 case 1: /* top, left */
1130 $rotate = 0; $flip = false; break;
1131 case 2: /* top, right */
1132 $rotate = 0; $flip = true; break;
1133 case 3: /* bottom, left */
1134 $rotate = 180; $flip = false; break;
1135 case 4: /* bottom, right */
1136 $rotate = 180; $flip = true; break;
1137 case 5: /* left side, top */
1138 $rotate = 90; $flip = true; break;
1139 case 6: /* right side, top */
1140 $rotate = 90; $flip = false; break;
1141 case 7: /* left side, bottom */
1142 $rotate = 270; $flip = true; break;
1143 case 8: /* right side, bottom */
1144 $rotate = 270; $flip = false; break;
1147 $src_img = @imagecreatefromjpeg($orig_image);
1150 print "Can't load image from ". $orig_image ."\n";
1154 /* grabs the height and width */
1155 $cur_width = imagesx($src_img);
1156 $cur_height = imagesy($src_img);
1158 // If requested width is more then the actual image width,
1159 // do not generate a thumbnail, instead safe the original
1160 // as thumbnail but with lower quality
1162 if($width >= $cur_width) {
1163 $result = imagejpeg($src_img, $thumb_image, 75);
1164 imagedestroy($src_img);
1168 // If the image will be rotate because EXIF orientation said so
1169 // 'virtually rotate' the image for further calculations
1170 if($rotate == 90 || $rotate == 270) {
1172 $cur_width = $cur_height;
1176 /* calculates aspect ratio */
1177 $aspect_ratio = $cur_height / $cur_width;
1180 if($aspect_ratio < 1) {
1182 $new_h = abs($new_w * $aspect_ratio);
1184 /* 'virtually' rotate the image and calculate it's ratio */
1185 $tmp_w = $cur_height;
1186 $tmp_h = $cur_width;
1187 /* now get the ratio from the 'rotated' image */
1188 $tmp_ratio = $tmp_h/$tmp_w;
1189 /* now calculate the new dimensions */
1191 $tmp_h = abs($tmp_w * $tmp_ratio);
1193 // now that we know, how high they photo should be, if it
1194 // gets rotated, use this high to scale the image
1196 $new_w = abs($new_h / $aspect_ratio);
1198 // If the image will be rotate because EXIF orientation said so
1199 // now 'virtually rotate' back the image for the image manipulation
1200 if($rotate == 90 || $rotate == 270) {
1207 /* creates new image of that size */
1208 $dst_img = imagecreatetruecolor($new_w, $new_h);
1210 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1212 /* copies resized portion of original image into new image */
1213 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1215 /* needs the image to be flipped horizontal? */
1219 for($x = 0; $x < $new_w; $x++) {
1220 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1225 $this->_debug("(ROTATE)");
1226 $dst_img = $this->rotateImage($dst_img, $rotate);
1229 /* write down new generated file */
1230 $result = imagejpeg($dst_img, $thumb_image, 75);
1232 /* free your mind */
1233 imagedestroy($dst_img);
1234 imagedestroy($src_img);
1236 if($result === false) {
1237 print "Can't write thumbnail ". $thumb_image ."\n";
1243 } // create_thumbnail()
1246 * return all exif meta data from the file
1248 public function get_meta_informations($file)
1250 return exif_read_data($file);
1252 } // get_meta_informations()
1255 * create phpfspot own sqlite database
1257 * this function creates phpfspots own sqlite database
1258 * if it does not exist yet. this own is used to store
1259 * some necessary informations (md5 sum's, ...).
1261 public function check_config_table()
1263 // if the config table doesn't exist yet, create it
1264 if(!$this->cfg_db->db_check_table_exists("images")) {
1265 $this->cfg_db->db_exec("
1266 CREATE TABLE images (
1267 img_idx int primary key,
1273 } // check_config_table
1276 * Generates a thumbnail from photo idx
1278 * This function will generate JPEG thumbnails from provided F-Spot photo
1281 * 1. Check if all thumbnail generations (width) are already in place and
1283 * 2. Check if the md5sum of the original file has changed
1284 * 3. Generate the thumbnails if needed
1286 public function gen_thumb($idx = 0, $force = 0)
1290 $resolutions = Array(
1291 $this->cfg->thumb_width,
1292 $this->cfg->photo_width,
1293 $this->cfg->mini_width,
1296 /* get details from F-Spot's database */
1297 $details = $this->get_photo_details($idx);
1299 /* calculate file MD5 sum */
1300 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1302 if(!file_exists($full_path)) {
1303 $this->_error("File ". $full_path ." does not exist\n");
1307 if(!is_readable($full_path)) {
1308 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1312 $file_md5 = md5_file($full_path);
1314 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1318 foreach($resolutions as $resolution) {
1320 $thumb_sub_path = substr($file_md5, 0, 2);
1321 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1323 if(!file_exists(dirname($thumb_path))) {
1324 mkdir(dirname($thumb_path), 0755);
1327 /* if the thumbnail file doesn't exist, create it */
1328 if(!file_exists($thumb_path)) {
1330 $this->_debug(" ". $resolution ."px");
1331 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1336 /* if the file hasn't changed there is no need to regen the thumb */
1337 elseif($file_md5 != $this->getMD5($idx) || $force) {
1339 $this->_debug(" ". $resolution ."px");
1340 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1348 $this->_debug(" already exist");
1351 /* set the new/changed MD5 sum for the current photo */
1353 $this->setMD5($idx, $file_md5);
1356 $this->_debug("\n");
1361 * returns stored md5 sum for a specific photo
1363 * this function queries the phpfspot database for a
1364 * stored MD5 checksum of the specified photo
1366 public function getMD5($idx)
1368 $result = $this->cfg_db->db_query("
1371 WHERE img_idx='". $idx ."'
1377 $img = $this->cfg_db->db_fetch_object($result);
1378 return $img['img_md5'];
1383 * set MD5 sum for the specific photo
1385 private function setMD5($idx, $md5)
1387 $result = $this->cfg_db->db_exec("
1388 REPLACE INTO images (img_idx, img_md5)
1389 VALUES ('". $idx ."', '". $md5 ."')
1395 * store current tag condition
1397 * this function stores the current tag condition
1398 * (AND or OR) in the users session variables
1400 public function setTagCondition($mode)
1402 $_SESSION['tag_condition'] = $mode;
1404 } // setTagCondition()
1407 * invoke tag & date search
1409 * this function will return all matching tags and store
1410 * them in the session variable selected_tags. furthermore
1411 * it also handles the date search.
1412 * getPhotoSelection() will then only return the matching
1415 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1419 $_SESSION['searchfor'] = $searchfor;
1420 $_SESSION['sort_order'] = $sort_order;
1422 $_SESSION['from_date'] = strtotime($from);
1424 unset($_SESSION['from_date']);
1426 $_SESSION['to_date'] = strtotime($to);
1428 unset($_SESSION['to_date']);
1430 if($searchfor != "") {
1431 /* new search, reset the current selected tags */
1432 $_SESSION['selected_tags'] = Array();
1433 foreach($this->avail_tags as $tag) {
1434 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1435 array_push($_SESSION['selected_tags'], $tag);
1444 * this function rotates the image according the
1447 private function rotateImage($img, $degrees)
1449 if(function_exists("imagerotate")) {
1450 $img = imagerotate($img, $degrees, 0);
1452 function imagerotate($src_img, $angle)
1454 $src_x = imagesx($src_img);
1455 $src_y = imagesy($src_img);
1456 if ($angle == 180) {
1460 elseif ($src_x <= $src_y) {
1464 elseif ($src_x >= $src_y) {
1469 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1470 imagealphablending($rotate, false);
1475 for ($y = 0; $y < ($src_y); $y++) {
1476 for ($x = 0; $x < ($src_x); $x++) {
1477 $color = imagecolorat($src_img, $x, $y);
1478 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1484 for ($y = 0; $y < ($src_y); $y++) {
1485 for ($x = 0; $x < ($src_x); $x++) {
1486 $color = imagecolorat($src_img, $x, $y);
1487 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1493 for ($y = 0; $y < ($src_y); $y++) {
1494 for ($x = 0; $x < ($src_x); $x++) {
1495 $color = imagecolorat($src_img, $x, $y);
1496 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1510 $img = imagerotate($img, $degrees);
1519 * return all assigned tags for the specified photo
1521 private function get_photo_tags($idx)
1523 $result = $this->db->db_query("
1526 INNER JOIN photo_tags pt
1528 WHERE pt.photo_id='". $idx ."'
1533 while($row = $this->db->db_fetch_object($result))
1534 $tags[$row['id']] = $row['name'];
1538 } // get_photo_tags()
1541 * create on-the-fly images with text within
1543 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1545 if (strlen($color) != 6)
1548 $int = hexdec($color);
1549 $h = imagefontheight($font);
1550 $fw = imagefontwidth($font);
1551 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1552 $lines = count($txt);
1553 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1554 $bg = imagecolorallocate($im, 255, 255, 255);
1555 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1558 foreach ($txt as $text) {
1559 $x = (($w - ($fw * strlen($text))) / 2);
1560 imagestring($im, $font, $x, $y, $text, $color);
1561 $y += ($h + $space);
1564 Header("Content-type: image/png");
1567 } // showTextImage()
1570 * check if all requirements are met
1572 private function checkRequirements()
1574 if(!function_exists("imagecreatefromjpeg")) {
1575 print "PHP GD library extension is missing<br />\n";
1579 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1580 print "PHP SQLite3 library extension is missing<br />\n";
1584 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1585 ini_set('track_errors', 1);
1586 @include_once 'HTML/AJAX/Server.php';
1587 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1588 print "PEAR HTML_AJAX package is missing<br />\n";
1591 @include_once 'Calendar/Calendar.php';
1592 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1593 print "PEAR Calendar package is missing<br />\n";
1596 ini_restore('track_errors');
1603 } // checkRequirements()
1605 private function _debug($text)
1607 if($this->fromcmd) {
1614 * check if specified MIME type is supported
1616 public function checkifImageSupported($mime)
1618 if(in_array($mime, Array("image/jpeg")))
1623 } // checkifImageSupported()
1625 public function _error($text)
1627 switch($this->cfg->logging) {
1629 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1636 error_log($text, 3, $his->cfg->log_file);
1643 * output calendard input fields
1645 private function get_calendar($mode)
1647 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1648 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1649 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1651 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1652 if(!isset($_SESSION[$mode .'_date']))
1653 $output.= " disabled=\"disabled\"";
1655 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1656 if(!isset($_SESSION[$mode .'_date']))
1657 $output.= " disabled=\"disabled\"";
1659 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1660 if(!isset($_SESSION[$mode .'_date']))
1661 $output.= " disabled=\"disabled\"";
1669 * output calendar matrix
1671 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1673 if (!isset($year)) $year = date('Y');
1674 if (!isset($month)) $month = date('m');
1675 if (!isset($day)) $day = date('d');
1680 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1681 require_once CALENDAR_ROOT.'Day.php';
1684 $month = new Calendar_Month_Weekdays($year,$month);
1687 $prevStamp = $month->prevMonth(true);
1688 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1689 $nextStamp = $month->nextMonth(true);
1690 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1692 $selectedDays = array (
1693 new Calendar_Day($year,$month,$day),
1694 new Calendar_Day($year,12,25),
1697 // Build the days in the month
1698 $month->build($selectedDays);
1700 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1701 $this->tmpl->assign('prev_month', $prev);
1702 $this->tmpl->assign('next_month', $next);
1704 while ( $day = $month->fetch() ) {
1706 if(!isset($matrix[$rows]))
1707 $matrix[$rows] = Array();
1711 $dayStamp = $day->thisDay(true);
1712 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1714 // isFirst() to find start of week
1715 if ( $day->isFirst() )
1718 if ( $day->isSelected() ) {
1719 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1720 } else if ( $day->isEmpty() ) {
1721 $string.= "<td> </td>\n";
1723 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1726 // isLast() to find end of week
1727 if ( $day->isLast() )
1728 $string.= "</tr>\n";
1730 $matrix[$rows][$cols] = $string;
1740 $this->tmpl->assign('matrix', $matrix);
1741 $this->tmpl->assign('rows', $rows);
1742 $this->tmpl->show("calendar.tpl");
1744 } // get_calendar_matrix()
1747 * output export page
1749 public function getExport($mode)
1751 $pictures = $this->getPhotoSelection();
1752 $current_tags = $this->getCurrentTags();
1754 foreach($pictures as $picture) {
1756 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1757 if($current_tags != "") {
1758 $orig_url.= "&tags=". $current_tags;
1760 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1761 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1764 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1769 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1770 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1774 // "[%pictureurl% %thumbnailurl%]"
1775 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1778 case 'MoinMoinList':
1779 // " * [%pictureurl% %thumbnailurl%]"
1780 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1791 public function getRSSFeed()
1793 Header("Content-type: text/xml; charset=utf-8");
1794 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1797 xmlns:media="http://search.yahoo.com/mrss/"
1798 xmlns:dc="http://purl.org/dc/elements/1.1/"
1801 <title>phpfspot</title>
1802 <description>phpfspot RSS feed</description>
1803 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1804 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1805 <generator>phpfspot</generator>
1808 $pictures = $this->getPhotoSelection();
1809 $current_tags = $this->getCurrentTags();
1811 foreach($pictures as $picture) {
1813 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1814 if($current_tags != "") {
1815 $orig_url.= "&tags=". $current_tags;
1817 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1818 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1821 $details = $this->get_photo_details($picture);
1823 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1824 $thumb_html = htmlspecialchars("
1825 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1827 ". $details['description']);
1829 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1830 $meta = $this->get_meta_informations($orig_path);
1831 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1835 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1836 <link><?php print htmlspecialchars($orig_url); ?></link>
1837 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1838 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1840 <?php print $thumb_html; ?>
1842 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1857 * return all selected tags as one string
1859 private function getCurrentTags()
1862 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1863 foreach($_SESSION['selected_tags'] as $tag)
1864 $current_tags.= $tag .",";
1865 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1867 return $current_tags;
1869 } // getCurrentTags()
1872 * return the current photo
1874 public function getCurrentPhoto()
1876 if(isset($_SESSION['current_photo'])) {
1877 print $_SESSION['current_photo'];
1879 } // getCurrentPhoto()
1882 * tells the client browser what to do
1884 * this function is getting called via AJAX by the
1885 * client browsers. it will tell them what they have
1886 * to do next. This is necessary for directly jumping
1887 * into photo index or single photo view when the are
1888 * requested with specific URLs
1890 public function whatToDo()
1892 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1893 return "show_photo";
1895 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1896 return "showpi_tags";
1898 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1902 return "nothing special";
1907 * return the current process-user
1909 private function getuid()
1911 if($uid = posix_getuid()) {
1912 if($user = posix_getpwuid($uid)) {
1913 return $user['name'];
1922 * returns a select-dropdown box to select photo index sort parameters
1924 private function get_sort_field()
1926 $output = "<select name=\"sort_order\">";
1927 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1928 $output.= "<option value=\"". $sort_order ."\"";
1929 if($sort_order == $_SESSION['sort_order']) {
1930 $output.= " selected=\"selected\"";
1932 $output.= ">". $sort_order ."</option>";
1934 $output.= "</select>";
1937 } // get_sort_field()
1940 * returns the currently selected sort order
1942 private function get_sort_order()
1944 switch($_SESSION['sort_order']) {
1946 return " ORDER BY p.time ASC";
1949 return " ORDER BY p.time DESC";
1952 return " ORDER BY p.name ASC";
1955 return " ORDER BY p.name DESC";
1959 } // get_sort_order()
1962 * return the next to be shown slide show image
1964 * this function returns the URL of the next image
1965 * in the slideshow sequence.
1967 public function getNextSlideShowImage()
1969 $all_photos = $this->getPhotoSelection();
1971 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1972 $_SESSION['slideshow_img'] = 0;
1974 $_SESSION['slideshow_img']++;
1976 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1978 } // getNextSlideShowImage()
1981 * return the previous to be shown slide show image
1983 * this function returns the URL of the previous image
1984 * in the slideshow sequence.
1986 public function getPrevSlideShowImage()
1988 $all_photos = $this->getPhotoSelection();
1990 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1991 $_SESSION['slideshow_img'] = 0;
1993 $_SESSION['slideshow_img']--;
1995 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1997 } // getPrevSlideShowImage()
1999 public function resetSlideShow()
2001 if(isset($_SESSION['slideshow_img']))
2002 unset($_SESSION['slideshow_img']);
2003 } // resetSlideShow()
2008 * this function will get all photos from the fspot
2009 * database and randomly return ONE entry
2011 * saddly there is yet no sqlite3 function which returns
2012 * the bulk result in array, so we have to fill up our
2015 public function get_random_photo()
2019 $result = $this->db->db_query("
2024 while($row = $this->db->db_fetch_object($result)) {
2025 array_push($all, $row['id']);
2028 return $all[array_rand($all)];
2030 } // get_random_photo()
2033 * validates provided date
2035 * this function validates if the provided date
2036 * contains a valid date and will return true
2039 public function isValidDate($date_str)
2041 $timestamp = strtotime($date_str);
2043 if(is_numeric($timestamp))
2051 * timestamp to string conversion
2053 private function ts2str($timestamp)
2055 return strftime("%Y-%m-%d", $timestamp);
2058 private function extractTags($tags_str)
2060 $not_validated = split(',', $_GET['tags']);
2061 $validated = array();
2063 foreach($not_validated as $tag) {
2064 if(is_numeric($tag))
2065 array_push($validated, $tag);
2073 * returns the full path to a thumbnail
2075 public function get_thumb_path($width, $photo)
2077 $md5 = $this->getMD5($photo);
2078 $sub_path = substr($md5, 0, 2);
2079 return $this->cfg->thumb_path
2087 } // get_thumb_path()
2090 * returns server's virtual host name
2092 private function get_server_name()
2094 return $_SERVER['SERVER_NAME'];
2095 } // get_server_name()
2098 * returns type of webprotocol which is
2101 private function get_web_protocol()
2103 if(!isset($_SERVER['HTTPS']))
2107 } // get_web_protocol()
2110 * return url to this phpfspot installation
2112 private function get_phpfspot_url()
2114 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2115 } // get_phpfspot_url()
2118 * check file exists and is readable
2120 * returns true, if everything is ok, otherwise false
2121 * if $silent is not set, this function will output and
2124 private function check_readable($file, $silent = null)
2126 if(!file_exists($file)) {
2128 print "File \"". $file ."\" does not exist.\n";
2132 if(!is_readable($file)) {
2134 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2140 } // check_readable()
2143 * check if all needed indices are present
2145 * this function checks, if some needed indices are already
2146 * present, or if not, create them on the fly. they are
2147 * necessary to speed up some queries like that one look for
2148 * all tags, when show_tags is specified in the configuration.
2150 private function checkDbIndices()
2152 $result = $this->db->db_exec("
2153 CREATE INDEX IF NOT EXISTS
2160 } // checkDbIndices()
2163 * retrive F-Spot database version
2165 * this function will return the F-Spot database version number
2166 * It is stored within the sqlite3 database in the table meta
2168 public function getFspotDBVersion()
2170 if($result = $this->db->db_fetchSingleRow("
2171 SELECT data as version
2174 name LIKE 'F-Spot Database Version'
2176 return $result['version'];
2180 } // getFspotDBVersion()
2183 * parse the provided URI and will returned the
2186 public function parse_uri($uri, $mode)
2188 if(($components = parse_url($uri)) !== false) {
2192 return basename($components['path']);
2195 return dirname($components['path']);
2198 return $components['path'];