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");
431 if(!is_readable($orig_path)) {
432 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
436 /* If the thumbnail doesn't exist yet, try to create it */
437 if(!file_exists($thumb_path)) {
438 $this->gen_thumb($photo, true);
439 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
442 /* get f-spot database meta information */
443 $meta = $this->get_meta_informations($orig_path);
445 /* If EXIF data are available, use them */
446 if(isset($meta['ExifImageWidth'])) {
447 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
449 $info = getimagesize($orig_path);
450 $meta_res = $info[0] ."x". $info[1];
453 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
454 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
455 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
457 $extern_link = "index.php?mode=showp&id=". $photo;
458 $current_tags = $this->getCurrentTags();
459 if($current_tags != "") {
460 $extern_link.= "&tags=". $current_tags;
462 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
463 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
466 $this->tmpl->assign('extern_link', $extern_link);
468 if(file_exists($thumb_path)) {
470 $info = getimagesize($thumb_path);
472 $this->tmpl->assign('description', $details['description']);
473 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
475 $this->tmpl->assign('width', $info[0]);
476 $this->tmpl->assign('height', $info[1]);
477 $this->tmpl->assign('ExifMadeOn', $meta_date);
478 $this->tmpl->assign('ExifMadeWith', $meta_make);
479 $this->tmpl->assign('ExifOrigResolution', $meta_res);
480 $this->tmpl->assign('ExifFileSize', $meta_size);
482 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
483 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
485 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
486 $this->tmpl->assign('current', $current);
489 $this->_error("Can't open file ". $thumb_path ."\n");
494 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
495 $this->tmpl->assign('prev_img', $previous_img);
499 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
500 $this->tmpl->assign('next_img', $next_img);
502 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
503 $this->tmpl->assign('photo_number', $i);
504 $this->tmpl->assign('photo_count', count($all_photos));
506 $this->tmpl->show("single_photo.tpl");
511 * all available tags and tag cloud
513 * this function outputs all available tags (time ordered)
514 * and in addition output them as tag cloud (tags which have
515 * many photos will appears more then others)
517 public function getAvailableTags()
523 $result = $this->db->db_query("
524 SELECT tag_id as id, count(tag_id) as quantity
534 while($row = $this->db->db_fetch_object($result)) {
535 $tags[$row['id']] = $row['quantity'];
538 // change these font sizes if you will
539 $max_size = 125; // max font size in %
540 $min_size = 75; // min font size in %
542 // get the largest and smallest array values
543 $max_qty = max(array_values($tags));
544 $min_qty = min(array_values($tags));
546 // find the range of values
547 $spread = $max_qty - $min_qty;
548 if (0 == $spread) { // we don't want to divide by zero
552 // determine the font-size increment
553 // this is the increase per tag quantity (times used)
554 $step = ($max_size - $min_size)/($spread);
556 // loop through our tag array
557 foreach ($tags as $key => $value) {
559 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
562 // calculate CSS font-size
563 // find the $value in excess of $min_qty
564 // multiply by the font-size increment ($size)
565 // and add the $min_size set above
566 $size = $min_size + (($value - $min_qty) * $step);
567 // uncomment if you want sizes in whole %:
570 if(isset($this->tags[$key])) {
571 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
576 $output = substr($output, 0, strlen($output)-2);
579 } // getAvailableTags()
582 * output all selected tags
584 * this function output all tags which have been selected
585 * by the user. the selected tags are stored in the
586 * session-variable $_SESSION['selected_tags']
588 public function getSelectedTags()
593 foreach($this->avail_tags as $tag)
595 // return all selected tags
596 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
597 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
601 $output = substr($output, 0, strlen($output)-2);
604 } // getSelectedTags()
607 * add tag to users session variable
609 * this function will add the specified to users current
610 * tag selection. if a date search has been made before
611 * it will be now cleared
613 public function addTag($tag)
615 if(!isset($_SESSION['selected_tags']))
616 $_SESSION['selected_tags'] = Array();
618 if(isset($_SESSION['searchfor']))
619 unset($_SESSION['searchfor']);
621 if(!in_array($tag, $_SESSION['selected_tags']))
622 array_push($_SESSION['selected_tags'], $tag);
627 * remove tag to users session variable
629 * this function removes the specified tag from
630 * users current tag selection
632 public function delTag($tag)
634 if(isset($_SESSION['searchfor']))
635 unset($_SESSION['searchfor']);
637 if(isset($_SESSION['selected_tags'])) {
638 $key = array_search($tag, $_SESSION['selected_tags']);
639 unset($_SESSION['selected_tags'][$key]);
640 sort($_SESSION['selected_tags']);
646 * reset tag selection
648 * if there is any tag selection, it will be
651 public function resetTags()
653 if(isset($_SESSION['selected_tags']))
654 unset($_SESSION['selected_tags']);
661 * if a specific photo was requested (external link)
662 * unset the session variable now
664 public function resetPhotoView()
666 if(isset($_SESSION['current_photo']))
667 unset($_SESSION['current_photo']);
669 } // resetPhotoView();
674 * if any tag search has taken place, reset
677 public function resetTagSearch()
679 if(isset($_SESSION['searchfor']))
680 unset($_SESSION['searchfor']);
682 } // resetTagSearch()
687 * if any date search has taken place, reset
690 public function resetDateSearch()
692 if(isset($_SESSION['from_date']))
693 unset($_SESSION['from_date']);
694 if(isset($_SESSION['to_date']))
695 unset($_SESSION['to_date']);
697 } // resetDateSearch();
700 * return all photo according selection
702 * this function returns all photos based on
703 * the tag-selection, tag- or date-search.
704 * the tag-search also has to take care of AND
705 * and OR conjunctions
707 public function getPhotoSelection()
709 $matched_photos = Array();
711 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
712 $from_date = $_SESSION['from_date'];
713 $to_date = $_SESSION['to_date'];
714 $additional_where_cond = "
715 p.time>='". $from_date ."'
717 p.time<='". $to_date ."'
721 if(isset($_SESSION['sort_order'])) {
722 $order_str = $this->get_sort_order();
725 /* return a search result */
726 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
728 SELECT DISTINCT pt1.photo_id
730 INNER JOIN photo_tags pt2
731 ON pt1.photo_id=pt2.photo_id
738 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
740 if(isset($additional_where_cond))
741 $query_str.= "AND ". $additional_where_cond ." ";
743 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
744 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
747 if(isset($order_str))
748 $query_str.= $order_str;
750 $result = $this->db->db_query($query_str);
751 while($row = $this->db->db_fetch_object($result)) {
752 array_push($matched_photos, $row['photo_id']);
754 return $matched_photos;
757 /* return according the selected tags */
758 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
760 foreach($_SESSION['selected_tags'] as $tag)
761 $selected.= $tag .",";
762 $selected = substr($selected, 0, strlen($selected)-1);
764 /* photo has to match at least on of the selected tags */
765 if($_SESSION['tag_condition'] == 'or') {
767 SELECT DISTINCT pt1.photo_id
769 INNER JOIN photo_tags pt2
770 ON pt1.photo_id=pt2.photo_id
775 WHERE pt1.tag_id IN (". $selected .")
777 if(isset($additional_where_cond))
778 $query_str.= "AND ". $additional_where_cond ." ";
780 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
781 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
784 if(isset($order_str))
785 $query_str.= $order_str;
787 /* photo has to match all selected tags */
788 elseif($_SESSION['tag_condition'] == 'and') {
790 if(count($_SESSION['selected_tags']) >= 32) {
791 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
792 print "evaluate your tag selection. Please remove some tags from your selection.\n";
796 /* Join together a table looking like
798 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
800 so the query can quickly return all images matching the
801 selected tags in an AND condition
806 SELECT DISTINCT pt1.photo_id
810 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
817 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
819 INNER JOIN photo_tags pt". ($i+2) ."
820 ON pt1.photo_id=pt". ($i+2) .".photo_id
827 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
828 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
830 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
833 if(isset($additional_where_cond))
834 $query_str.= "AND ". $additional_where_cond;
836 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
837 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
840 if(isset($order_str))
841 $query_str.= $order_str;
845 $result = $this->db->db_query($query_str);
846 while($row = $this->db->db_fetch_object($result)) {
847 array_push($matched_photos, $row['photo_id']);
849 return $matched_photos;
852 /* return all available photos */
854 SELECT DISTINCT photo_id
861 if(isset($additional_where_cond))
862 $query_str.= "WHERE ". $additional_where_cond ." ";
864 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
865 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
868 if(isset($order_str))
869 $query_str.= $order_str;
871 $result = $this->db->db_query($query_str);
872 while($row = $this->db->db_fetch_object($result)) {
873 array_push($matched_photos, $row['photo_id']);
875 return $matched_photos;
877 } // getPhotoSelection()
880 * control HTML ouput for photo index
882 * this function provides all the necessary information
883 * for the photo index template.
885 public function showPhotoIndex()
887 $photos = $this->getPhotoSelection();
889 $count = count($photos);
891 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
892 $anchor = $_SESSION['begin_with'];
894 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
900 elseif($this->cfg->rows_per_page > 0) {
902 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
907 $begin_with = $_SESSION['begin_with'];
909 // verify $begin_with - perhaps the thumbs-per-rows or
910 // rows-per-page variables have changed or the jump back
911 // from a photo wasn't exact - so calculate the real new
913 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
914 for($i = 0; $i <= $count; $i+=$multiplicator) {
915 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
922 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
928 $images[$rows] = Array();
929 $img_height[$rows] = Array();
930 $img_width[$rows] = Array();
931 $img_id[$rows] = Array();
932 $img_name[$rows] = Array();
933 $img_title = Array();
935 for($i = $begin_with; $i < $end_with; $i++) {
937 if(isset($photos[$i])) {
939 $images[$rows][$cols] = $photos[$i];
940 $img_id[$rows][$cols] = $i;
941 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
942 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
944 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
946 if(file_exists($thumb_path)) {
947 $info = getimagesize($thumb_path);
948 $img_width[$rows][$cols] = $info[0];
949 $img_height[$rows][$cols] = $info[1];
952 if($cols == $this->cfg->thumbs_per_row-1) {
955 $images[$rows] = Array();
956 $img_width[$rows] = Array();
957 $img_height[$rows] = Array();
965 // +1 for for smarty's selection iteration
968 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
969 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
971 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
972 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
973 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
976 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
977 $this->tmpl->assign('tag_result', 1);
980 /* do we have to display the page selector ? */
981 if($this->cfg->rows_per_page != 0) {
985 /* calculate the page switchers */
986 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
987 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
990 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
991 if($end_with < $count)
992 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
994 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
995 $last_page = ceil($count / $photo_per_page);
997 /* get the current selected page */
998 if($begin_with == 0) {
1002 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1009 for($i = 1; $i <= $last_page; $i++) {
1011 if($current_page == $i)
1012 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1013 elseif($current_page-1 == $i || $current_page+1 == $i)
1014 $style = "style=\"font-size: 105%;\"";
1015 elseif(($current_page-5 >= $i) && ($i != 1) ||
1016 ($current_page+5 <= $i) && ($i != $last_page))
1017 $style = "style=\"font-size: 75%;\"";
1021 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1024 $select.= ">". $i ."</a> ";
1026 // until 9 pages we show the selector from 1-9
1027 if($last_page <= 9) {
1028 $page_select.= $select;
1031 if($i == 1 /* first page */ ||
1032 $i == $last_page /* last page */ ||
1033 $i == $current_page /* current page */ ||
1034 $i == ceil($last_page * 0.25) /* first quater */ ||
1035 $i == ceil($last_page * 0.5) /* half */ ||
1036 $i == ceil($last_page * 0.75) /* third quater */ ||
1037 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1038 (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 */ ||
1039 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1040 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1042 $page_select.= $select;
1050 $page_select.= "......... ";
1055 /* only show the page selector if we have more then one page */
1057 $this->tmpl->assign('page_selector', $page_select);
1061 $current_tags = $this->getCurrentTags();
1062 $extern_link = "index.php?mode=showpi";
1063 $rss_link = "index.php?mode=rss";
1064 if($current_tags != "") {
1065 $extern_link.= "&tags=". $current_tags;
1066 $rss_link.= "&tags=". $current_tags;
1068 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1069 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1070 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1073 $export_link = "index.php?mode=export";
1074 $slideshow_link = "index.php?mode=slideshow";
1076 $this->tmpl->assign('extern_link', $extern_link);
1077 $this->tmpl->assign('slideshow_link', $slideshow_link);
1078 $this->tmpl->assign('export_link', $export_link);
1079 $this->tmpl->assign('rss_link', $rss_link);
1080 $this->tmpl->assign('count', $count);
1081 $this->tmpl->assign('width', $this->cfg->thumb_width);
1082 $this->tmpl->assign('images', $images);
1083 $this->tmpl->assign('img_width', $img_width);
1084 $this->tmpl->assign('img_height', $img_height);
1085 $this->tmpl->assign('img_id', $img_id);
1086 $this->tmpl->assign('img_name', $img_name);
1087 $this->tmpl->assign('img_title', $img_title);
1088 $this->tmpl->assign('rows', $rows);
1089 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1091 $this->tmpl->show("photo_index.tpl");
1094 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1096 } // showPhotoIndex()
1099 * show credit template
1101 public function showCredits()
1103 $this->tmpl->assign('version', $this->cfg->version);
1104 $this->tmpl->assign('product', $this->cfg->product);
1105 $this->tmpl->assign('db_version', $this->dbver);
1106 $this->tmpl->show("credits.tpl");
1111 * create_thumbnails for the requested width
1113 * this function creates image thumbnails of $orig_image
1114 * stored as $thumb_image. It will check if the image is
1115 * in a supported format, if necessary rotate the image
1116 * (based on EXIF orientation meta headers) and re-sizing.
1118 public function create_thumbnail($orig_image, $thumb_image, $width)
1120 if(!file_exists($orig_image)) {
1124 $details = getimagesize($orig_image);
1126 /* check if original photo is a support image type */
1127 if(!$this->checkifImageSupported($details['mime']))
1130 $meta = $this->get_meta_informations($orig_image);
1135 switch($meta['Orientation']) {
1137 case 1: /* top, left */
1138 $rotate = 0; $flip = false; break;
1139 case 2: /* top, right */
1140 $rotate = 0; $flip = true; break;
1141 case 3: /* bottom, left */
1142 $rotate = 180; $flip = false; break;
1143 case 4: /* bottom, right */
1144 $rotate = 180; $flip = true; break;
1145 case 5: /* left side, top */
1146 $rotate = 90; $flip = true; break;
1147 case 6: /* right side, top */
1148 $rotate = 90; $flip = false; break;
1149 case 7: /* left side, bottom */
1150 $rotate = 270; $flip = true; break;
1151 case 8: /* right side, bottom */
1152 $rotate = 270; $flip = false; break;
1155 $src_img = @imagecreatefromjpeg($orig_image);
1158 print "Can't load image from ". $orig_image ."\n";
1162 /* grabs the height and width */
1163 $cur_width = imagesx($src_img);
1164 $cur_height = imagesy($src_img);
1166 // If requested width is more then the actual image width,
1167 // do not generate a thumbnail, instead safe the original
1168 // as thumbnail but with lower quality
1170 if($width >= $cur_width) {
1171 $result = imagejpeg($src_img, $thumb_image, 75);
1172 imagedestroy($src_img);
1176 // If the image will be rotate because EXIF orientation said so
1177 // 'virtually rotate' the image for further calculations
1178 if($rotate == 90 || $rotate == 270) {
1180 $cur_width = $cur_height;
1184 /* calculates aspect ratio */
1185 $aspect_ratio = $cur_height / $cur_width;
1188 if($aspect_ratio < 1) {
1190 $new_h = abs($new_w * $aspect_ratio);
1192 /* 'virtually' rotate the image and calculate it's ratio */
1193 $tmp_w = $cur_height;
1194 $tmp_h = $cur_width;
1195 /* now get the ratio from the 'rotated' image */
1196 $tmp_ratio = $tmp_h/$tmp_w;
1197 /* now calculate the new dimensions */
1199 $tmp_h = abs($tmp_w * $tmp_ratio);
1201 // now that we know, how high they photo should be, if it
1202 // gets rotated, use this high to scale the image
1204 $new_w = abs($new_h / $aspect_ratio);
1206 // If the image will be rotate because EXIF orientation said so
1207 // now 'virtually rotate' back the image for the image manipulation
1208 if($rotate == 90 || $rotate == 270) {
1215 /* creates new image of that size */
1216 $dst_img = imagecreatetruecolor($new_w, $new_h);
1218 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1220 /* copies resized portion of original image into new image */
1221 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1223 /* needs the image to be flipped horizontal? */
1227 for($x = 0; $x < $new_w; $x++) {
1228 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1233 $this->_debug("(ROTATE)");
1234 $dst_img = $this->rotateImage($dst_img, $rotate);
1237 /* write down new generated file */
1238 $result = imagejpeg($dst_img, $thumb_image, 75);
1240 /* free your mind */
1241 imagedestroy($dst_img);
1242 imagedestroy($src_img);
1244 if($result === false) {
1245 print "Can't write thumbnail ". $thumb_image ."\n";
1251 } // create_thumbnail()
1254 * return all exif meta data from the file
1256 public function get_meta_informations($file)
1258 return exif_read_data($file);
1260 } // get_meta_informations()
1263 * create phpfspot own sqlite database
1265 * this function creates phpfspots own sqlite database
1266 * if it does not exist yet. this own is used to store
1267 * some necessary informations (md5 sum's, ...).
1269 public function check_config_table()
1271 // if the config table doesn't exist yet, create it
1272 if(!$this->cfg_db->db_check_table_exists("images")) {
1273 $this->cfg_db->db_exec("
1274 CREATE TABLE images (
1275 img_idx int primary key,
1281 } // check_config_table
1284 * Generates a thumbnail from photo idx
1286 * This function will generate JPEG thumbnails from provided F-Spot photo
1289 * 1. Check if all thumbnail generations (width) are already in place and
1291 * 2. Check if the md5sum of the original file has changed
1292 * 3. Generate the thumbnails if needed
1294 public function gen_thumb($idx = 0, $force = 0)
1298 $resolutions = Array(
1299 $this->cfg->thumb_width,
1300 $this->cfg->photo_width,
1301 $this->cfg->mini_width,
1304 /* get details from F-Spot's database */
1305 $details = $this->get_photo_details($idx);
1307 /* calculate file MD5 sum */
1308 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1310 if(!file_exists($full_path)) {
1311 $this->_error("File ". $full_path ." does not exist\n");
1315 if(!is_readable($full_path)) {
1316 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1320 $file_md5 = md5_file($full_path);
1322 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1326 foreach($resolutions as $resolution) {
1328 $thumb_sub_path = substr($file_md5, 0, 2);
1329 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1331 if(!file_exists(dirname($thumb_path))) {
1332 mkdir(dirname($thumb_path), 0755);
1335 /* if the thumbnail file doesn't exist, create it */
1336 if(!file_exists($thumb_path)) {
1338 $this->_debug(" ". $resolution ."px");
1339 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1344 /* if the file hasn't changed there is no need to regen the thumb */
1345 elseif($file_md5 != $this->getMD5($idx) || $force) {
1347 $this->_debug(" ". $resolution ."px");
1348 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1356 $this->_debug(" already exist");
1359 /* set the new/changed MD5 sum for the current photo */
1361 $this->setMD5($idx, $file_md5);
1364 $this->_debug("\n");
1369 * returns stored md5 sum for a specific photo
1371 * this function queries the phpfspot database for a
1372 * stored MD5 checksum of the specified photo
1374 public function getMD5($idx)
1376 $result = $this->cfg_db->db_query("
1379 WHERE img_idx='". $idx ."'
1385 $img = $this->cfg_db->db_fetch_object($result);
1386 return $img['img_md5'];
1391 * set MD5 sum for the specific photo
1393 private function setMD5($idx, $md5)
1395 $result = $this->cfg_db->db_exec("
1396 REPLACE INTO images (img_idx, img_md5)
1397 VALUES ('". $idx ."', '". $md5 ."')
1403 * store current tag condition
1405 * this function stores the current tag condition
1406 * (AND or OR) in the users session variables
1408 public function setTagCondition($mode)
1410 $_SESSION['tag_condition'] = $mode;
1412 } // setTagCondition()
1415 * invoke tag & date search
1417 * this function will return all matching tags and store
1418 * them in the session variable selected_tags. furthermore
1419 * it also handles the date search.
1420 * getPhotoSelection() will then only return the matching
1423 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1427 $_SESSION['searchfor'] = $searchfor;
1428 $_SESSION['sort_order'] = $sort_order;
1430 $_SESSION['from_date'] = strtotime($from);
1432 unset($_SESSION['from_date']);
1434 $_SESSION['to_date'] = strtotime($to);
1436 unset($_SESSION['to_date']);
1438 if($searchfor != "") {
1439 /* new search, reset the current selected tags */
1440 $_SESSION['selected_tags'] = Array();
1441 foreach($this->avail_tags as $tag) {
1442 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1443 array_push($_SESSION['selected_tags'], $tag);
1452 * this function rotates the image according the
1455 private function rotateImage($img, $degrees)
1457 if(function_exists("imagerotate")) {
1458 $img = imagerotate($img, $degrees, 0);
1460 function imagerotate($src_img, $angle)
1462 $src_x = imagesx($src_img);
1463 $src_y = imagesy($src_img);
1464 if ($angle == 180) {
1468 elseif ($src_x <= $src_y) {
1472 elseif ($src_x >= $src_y) {
1477 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1478 imagealphablending($rotate, false);
1483 for ($y = 0; $y < ($src_y); $y++) {
1484 for ($x = 0; $x < ($src_x); $x++) {
1485 $color = imagecolorat($src_img, $x, $y);
1486 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1492 for ($y = 0; $y < ($src_y); $y++) {
1493 for ($x = 0; $x < ($src_x); $x++) {
1494 $color = imagecolorat($src_img, $x, $y);
1495 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1501 for ($y = 0; $y < ($src_y); $y++) {
1502 for ($x = 0; $x < ($src_x); $x++) {
1503 $color = imagecolorat($src_img, $x, $y);
1504 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1518 $img = imagerotate($img, $degrees);
1527 * return all assigned tags for the specified photo
1529 private function get_photo_tags($idx)
1531 $result = $this->db->db_query("
1534 INNER JOIN photo_tags pt
1536 WHERE pt.photo_id='". $idx ."'
1541 while($row = $this->db->db_fetch_object($result))
1542 $tags[$row['id']] = $row['name'];
1546 } // get_photo_tags()
1549 * create on-the-fly images with text within
1551 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1553 if (strlen($color) != 6)
1556 $int = hexdec($color);
1557 $h = imagefontheight($font);
1558 $fw = imagefontwidth($font);
1559 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1560 $lines = count($txt);
1561 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1562 $bg = imagecolorallocate($im, 255, 255, 255);
1563 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1566 foreach ($txt as $text) {
1567 $x = (($w - ($fw * strlen($text))) / 2);
1568 imagestring($im, $font, $x, $y, $text, $color);
1569 $y += ($h + $space);
1572 Header("Content-type: image/png");
1575 } // showTextImage()
1578 * check if all requirements are met
1580 private function checkRequirements()
1582 if(!function_exists("imagecreatefromjpeg")) {
1583 print "PHP GD library extension is missing<br />\n";
1587 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1588 print "PHP SQLite3 library extension is missing<br />\n";
1592 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1593 ini_set('track_errors', 1);
1594 @include_once 'HTML/AJAX/Server.php';
1595 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1596 print "PEAR HTML_AJAX package is missing<br />\n";
1599 @include_once 'Calendar/Calendar.php';
1600 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1601 print "PEAR Calendar package is missing<br />\n";
1604 ini_restore('track_errors');
1611 } // checkRequirements()
1613 private function _debug($text)
1615 if($this->fromcmd) {
1622 * check if specified MIME type is supported
1624 public function checkifImageSupported($mime)
1626 if(in_array($mime, Array("image/jpeg")))
1631 } // checkifImageSupported()
1633 public function _error($text)
1635 switch($this->cfg->logging) {
1637 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1644 error_log($text, 3, $his->cfg->log_file);
1651 * output calendard input fields
1653 private function get_calendar($mode)
1655 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1656 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1657 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1659 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1660 if(!isset($_SESSION[$mode .'_date']))
1661 $output.= " disabled=\"disabled\"";
1663 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1664 if(!isset($_SESSION[$mode .'_date']))
1665 $output.= " disabled=\"disabled\"";
1667 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1668 if(!isset($_SESSION[$mode .'_date']))
1669 $output.= " disabled=\"disabled\"";
1677 * output calendar matrix
1679 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1681 if (!isset($year)) $year = date('Y');
1682 if (!isset($month)) $month = date('m');
1683 if (!isset($day)) $day = date('d');
1688 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1689 require_once CALENDAR_ROOT.'Day.php';
1692 $month = new Calendar_Month_Weekdays($year,$month);
1695 $prevStamp = $month->prevMonth(true);
1696 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1697 $nextStamp = $month->nextMonth(true);
1698 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1700 $selectedDays = array (
1701 new Calendar_Day($year,$month,$day),
1702 new Calendar_Day($year,12,25),
1705 // Build the days in the month
1706 $month->build($selectedDays);
1708 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1709 $this->tmpl->assign('prev_month', $prev);
1710 $this->tmpl->assign('next_month', $next);
1712 while ( $day = $month->fetch() ) {
1714 if(!isset($matrix[$rows]))
1715 $matrix[$rows] = Array();
1719 $dayStamp = $day->thisDay(true);
1720 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1722 // isFirst() to find start of week
1723 if ( $day->isFirst() )
1726 if ( $day->isSelected() ) {
1727 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1728 } else if ( $day->isEmpty() ) {
1729 $string.= "<td> </td>\n";
1731 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1734 // isLast() to find end of week
1735 if ( $day->isLast() )
1736 $string.= "</tr>\n";
1738 $matrix[$rows][$cols] = $string;
1748 $this->tmpl->assign('matrix', $matrix);
1749 $this->tmpl->assign('rows', $rows);
1750 $this->tmpl->show("calendar.tpl");
1752 } // get_calendar_matrix()
1755 * output export page
1757 public function getExport($mode)
1759 $pictures = $this->getPhotoSelection();
1760 $current_tags = $this->getCurrentTags();
1762 foreach($pictures as $picture) {
1764 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1765 if($current_tags != "") {
1766 $orig_url.= "&tags=". $current_tags;
1768 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1769 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1772 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1777 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1778 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1782 // "[%pictureurl% %thumbnailurl%]"
1783 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1786 case 'MoinMoinList':
1787 // " * [%pictureurl% %thumbnailurl%]"
1788 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1799 public function getRSSFeed()
1801 Header("Content-type: text/xml; charset=utf-8");
1802 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1805 xmlns:media="http://search.yahoo.com/mrss/"
1806 xmlns:dc="http://purl.org/dc/elements/1.1/"
1809 <title>phpfspot</title>
1810 <description>phpfspot RSS feed</description>
1811 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1812 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1813 <generator>phpfspot</generator>
1816 $pictures = $this->getPhotoSelection();
1817 $current_tags = $this->getCurrentTags();
1819 foreach($pictures as $picture) {
1821 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1822 if($current_tags != "") {
1823 $orig_url.= "&tags=". $current_tags;
1825 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1826 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1829 $details = $this->get_photo_details($picture);
1831 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1832 $thumb_html = htmlspecialchars("
1833 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1835 ". $details['description']);
1837 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1838 $meta = $this->get_meta_informations($orig_path);
1839 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1843 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1844 <link><?php print htmlspecialchars($orig_url); ?></link>
1845 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1846 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1848 <?php print $thumb_html; ?>
1850 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1865 * return all selected tags as one string
1867 private function getCurrentTags()
1870 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1871 foreach($_SESSION['selected_tags'] as $tag)
1872 $current_tags.= $tag .",";
1873 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1875 return $current_tags;
1877 } // getCurrentTags()
1880 * return the current photo
1882 public function getCurrentPhoto()
1884 if(isset($_SESSION['current_photo'])) {
1885 print $_SESSION['current_photo'];
1887 } // getCurrentPhoto()
1890 * tells the client browser what to do
1892 * this function is getting called via AJAX by the
1893 * client browsers. it will tell them what they have
1894 * to do next. This is necessary for directly jumping
1895 * into photo index or single photo view when the are
1896 * requested with specific URLs
1898 public function whatToDo()
1900 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1901 return "show_photo";
1903 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1904 return "showpi_tags";
1906 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1910 return "nothing special";
1915 * return the current process-user
1917 private function getuid()
1919 if($uid = posix_getuid()) {
1920 if($user = posix_getpwuid($uid)) {
1921 return $user['name'];
1930 * returns a select-dropdown box to select photo index sort parameters
1932 private function get_sort_field()
1934 $output = "<select name=\"sort_order\">";
1935 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1936 $output.= "<option value=\"". $sort_order ."\"";
1937 if($sort_order == $_SESSION['sort_order']) {
1938 $output.= " selected=\"selected\"";
1940 $output.= ">". $sort_order ."</option>";
1942 $output.= "</select>";
1945 } // get_sort_field()
1948 * returns the currently selected sort order
1950 private function get_sort_order()
1952 switch($_SESSION['sort_order']) {
1954 return " ORDER BY p.time ASC";
1957 return " ORDER BY p.time DESC";
1960 if($this->dbver < 9) {
1961 return " ORDER BY p.name ASC";
1964 return " ORDER BY basename(p.uri) ASC";
1968 if($this->dbver < 9) {
1969 return " ORDER BY p.name DESC";
1972 return " ORDER BY basename(p.uri) DESC";
1977 } // get_sort_order()
1980 * return the next to be shown slide show image
1982 * this function returns the URL of the next image
1983 * in the slideshow sequence.
1985 public function getNextSlideShowImage()
1987 $all_photos = $this->getPhotoSelection();
1989 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1990 $_SESSION['slideshow_img'] = 0;
1992 $_SESSION['slideshow_img']++;
1994 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1996 } // getNextSlideShowImage()
1999 * return the previous to be shown slide show image
2001 * this function returns the URL of the previous image
2002 * in the slideshow sequence.
2004 public function getPrevSlideShowImage()
2006 $all_photos = $this->getPhotoSelection();
2008 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2009 $_SESSION['slideshow_img'] = 0;
2011 $_SESSION['slideshow_img']--;
2013 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2015 } // getPrevSlideShowImage()
2017 public function resetSlideShow()
2019 if(isset($_SESSION['slideshow_img']))
2020 unset($_SESSION['slideshow_img']);
2021 } // resetSlideShow()
2026 * this function will get all photos from the fspot
2027 * database and randomly return ONE entry
2029 * saddly there is yet no sqlite3 function which returns
2030 * the bulk result in array, so we have to fill up our
2033 public function get_random_photo()
2037 $result = $this->db->db_query("
2042 while($row = $this->db->db_fetch_object($result)) {
2043 array_push($all, $row['id']);
2046 return $all[array_rand($all)];
2048 } // get_random_photo()
2051 * validates provided date
2053 * this function validates if the provided date
2054 * contains a valid date and will return true
2057 public function isValidDate($date_str)
2059 $timestamp = strtotime($date_str);
2061 if(is_numeric($timestamp))
2069 * timestamp to string conversion
2071 private function ts2str($timestamp)
2073 return strftime("%Y-%m-%d", $timestamp);
2076 private function extractTags($tags_str)
2078 $not_validated = split(',', $_GET['tags']);
2079 $validated = array();
2081 foreach($not_validated as $tag) {
2082 if(is_numeric($tag))
2083 array_push($validated, $tag);
2091 * returns the full path to a thumbnail
2093 public function get_thumb_path($width, $photo)
2095 $md5 = $this->getMD5($photo);
2096 $sub_path = substr($md5, 0, 2);
2097 return $this->cfg->thumb_path
2105 } // get_thumb_path()
2108 * returns server's virtual host name
2110 private function get_server_name()
2112 return $_SERVER['SERVER_NAME'];
2113 } // get_server_name()
2116 * returns type of webprotocol which is
2119 private function get_web_protocol()
2121 if(!isset($_SERVER['HTTPS']))
2125 } // get_web_protocol()
2128 * return url to this phpfspot installation
2130 private function get_phpfspot_url()
2132 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2133 } // get_phpfspot_url()
2136 * check file exists and is readable
2138 * returns true, if everything is ok, otherwise false
2139 * if $silent is not set, this function will output and
2142 private function check_readable($file, $silent = null)
2144 if(!file_exists($file)) {
2146 print "File \"". $file ."\" does not exist.\n";
2150 if(!is_readable($file)) {
2152 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2158 } // check_readable()
2161 * check if all needed indices are present
2163 * this function checks, if some needed indices are already
2164 * present, or if not, create them on the fly. they are
2165 * necessary to speed up some queries like that one look for
2166 * all tags, when show_tags is specified in the configuration.
2168 private function checkDbIndices()
2170 $result = $this->db->db_exec("
2171 CREATE INDEX IF NOT EXISTS
2178 } // checkDbIndices()
2181 * retrive F-Spot database version
2183 * this function will return the F-Spot database version number
2184 * It is stored within the sqlite3 database in the table meta
2186 public function getFspotDBVersion()
2188 if($result = $this->db->db_fetchSingleRow("
2189 SELECT data as version
2192 name LIKE 'F-Spot Database Version'
2194 return $result['version'];
2198 } // getFspotDBVersion()
2201 * parse the provided URI and will returned the
2204 public function parse_uri($uri, $mode)
2206 if(($components = parse_url($uri)) !== false) {
2210 return basename($components['path']);
2213 return dirname($components['path']);
2216 return $components['path'];