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 $this->sort_orders= array(
53 'date_asc' => 'Date ↑',
54 'date_desc' => 'Date ↓',
55 'name_asc' => 'Name ↑',
56 'name_desc' => 'Name ↓'
59 /* Check necessary requirements */
60 if(!$this->checkRequirements()) {
64 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
65 if(!is_writeable($this->cfg->fspot_db)) {
66 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
70 $this->dbver = $this->getFspotDBVersion();
72 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
73 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
77 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
78 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
82 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
83 if(!is_writeable($this->cfg->phpfspot_db)) {
84 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
88 $this->check_config_table();
90 /* include Smarty template engine */
91 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
94 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
95 /* overload Smarty class if our own template handler */
96 require_once "phpfspot_tmpl.php";
97 $this->tmpl = new PHPFSPOT_TMPL($this);
99 /* check if all necessary indices exist */
100 $this->checkDbIndices();
102 /* if session is not yet started, do it now */
103 if(session_id() == "")
106 if(!isset($_SESSION['tag_condition']))
107 $_SESSION['tag_condition'] = 'or';
109 if(!isset($_SESSION['sort_order']))
110 $_SESSION['sort_order'] = 'date_asc';
112 if(!isset($_SESSION['searchfor']))
113 $_SESSION['searchfor'] = '';
115 // if begin_with is still set but rows_per_page is now 0, unset it
116 if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
117 unset($_SESSION['begin_with']);
121 public function __destruct()
127 * show - generate html output
129 * this function can be called after the constructor has
130 * prepared everyhing. it will load the index.tpl smarty
131 * template. if necessary it will registere pre-selects
132 * (photo index, photo, tag search, date search) into
135 public function show()
137 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
138 $this->tmpl->assign('page_title', $this->cfg->page_title);
139 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
140 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
142 if(isset($_GET['mode'])) {
144 $_SESSION['start_action'] = $_GET['mode'];
146 switch($_GET['mode']) {
148 if(isset($_GET['tags'])) {
149 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
151 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
152 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
154 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
155 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
159 if(isset($_GET['tags'])) {
160 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
161 $_SESSION['start_action'] = 'showp';
163 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
164 $_SESSION['current_photo'] = $_GET['id'];
165 $_SESSION['start_action'] = 'showp';
167 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
168 $_SESSION['from_date'] = strtotime($_GET['from_date']);
170 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
171 $_SESSION['to_date'] = strtotime($_GET['to_date']);
175 $this->tmpl->show("export.tpl");
179 $this->tmpl->show("slideshow.tpl");
183 if(isset($_GET['tags'])) {
184 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
186 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
187 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
189 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
190 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
198 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
199 $this->tmpl->assign('date_search_enabled', true);
201 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
202 $this->tmpl->assign('from_date', $this->get_calendar('from'));
203 $this->tmpl->assign('to_date', $this->get_calendar('to'));
204 $this->tmpl->assign('content_page', 'welcome.tpl');
205 $this->tmpl->show("index.tpl");
210 * get_tags - grab all tags of f-spot's database
212 * this function will get all available tags from
213 * the f-spot database and store them within two
214 * arrays within this class for later usage. in
215 * fact, if the user requests (hide_tags) it will
216 * opt-out some of them.
218 * this function is getting called once by show()
220 private function get_tags()
222 $this->avail_tags = Array();
225 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
228 DISTINCT t1.id as id, t1.name as name
231 INNER JOIN photo_tags
232 pt2 ON pt1.photo_id=pt2.photo_id
238 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
240 t1.sort_priority ASC";
242 $result = $this->db->db_query($query_str);
246 $result = $this->db->db_query("
249 ORDER BY sort_priority ASC
253 while($row = $this->db->db_fetch_object($result)) {
255 $tag_id = $row['id'];
256 $tag_name = $row['name'];
258 /* if the user has specified to ignore this tag in phpfspot's
259 configuration, ignore it here so it does not get added to
262 if(in_array($row['name'], $this->cfg->hide_tags))
265 /* if you include the following if-clause and the user has specified
266 to only show certain tags which are specified in phpfspot's
267 configuration, ignore all others so they will not be added to the
269 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
270 !in_array($row['name'], $this->cfg->show_tags))
274 $this->tags[$tag_id] = $tag_name;
275 $this->avail_tags[$count] = $tag_id;
283 * extract all photo details
285 * retrieve all available details from f-spot's
286 * database and return them as object
288 public function get_photo_details($idx)
290 if($this->dbver < 9) {
292 SELECT p.id, p.name, p.time, p.directory_path, p.description
298 SELECT p.id, p.uri, p.time, p.description
303 /* if show_tags is set, only return details for photos which
304 are specified to be shown
306 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
308 INNER JOIN photo_tags pt
312 WHERE p.id='". $idx ."'
313 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
317 WHERE p.id='". $idx ."'
321 if($result = $this->db->db_query($query_str)) {
323 $row = $this->db->db_fetch_object($result);
325 if($this->dbver < 9) {
326 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
335 } // get_photo_details
338 * returns aligned photo names
340 * this function returns aligned (length) names for
341 * an specific photo. If the length of the name exceeds
342 * $limit the name will be shrinked (...)
344 public function getPhotoName($idx, $limit = 0)
346 if($details = $this->get_photo_details($idx)) {
347 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
348 $name = $this->shrink_text($long_name, $limit);
358 * shrink text according provided limit
360 * If the length of the name exceeds $limit the
361 * text will be shortend and some content in between
362 * will be replaced with "..."
364 private function shrink_text($text, $limit)
366 if($limit != 0 && strlen($text) > $limit) {
367 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
375 * translate f-spoth photo path
377 * as the full-qualified path recorded in the f-spot database
378 * is usally not the same as on the webserver, this function
379 * will replace the path with that one specified in the cfg
381 public function translate_path($path, $width = 0)
383 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
388 * control HTML ouput for a single photo
390 * this function provides all the necessary information
391 * for the single photo template.
393 public function showPhoto($photo)
395 /* get all photos from the current photo selection */
396 $all_photos = $this->getPhotoSelection();
397 $count = count($all_photos);
399 for($i = 0; $i < $count; $i++) {
401 // $get_next will be set, when the photo which has to
402 // be displayed has been found - this means that the
403 // next available is in fact the NEXT image (for the
405 if(isset($get_next)) {
406 $next_img = $all_photos[$i];
410 /* the next photo is our NEXT photo */
411 if($all_photos[$i] == $photo) {
415 $previous_img = $all_photos[$i];
418 if($photo == $all_photos[$i]) {
423 $details = $this->get_photo_details($photo);
430 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
431 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
433 if(!file_exists($orig_path)) {
434 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
438 if(!is_readable($orig_path)) {
439 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
443 /* If the thumbnail doesn't exist yet, try to create it */
444 if(!file_exists($thumb_path)) {
445 $this->gen_thumb($photo, true);
446 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
449 /* get f-spot database meta information */
450 $meta = $this->get_meta_informations($orig_path);
452 /* If EXIF data are available, use them */
453 if(isset($meta['ExifImageWidth'])) {
454 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
456 $info = getimagesize($orig_path);
457 $meta_res = $info[0] ."x". $info[1];
460 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
461 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
462 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
464 $extern_link = "index.php?mode=showp&id=". $photo;
465 $current_tags = $this->getCurrentTags();
466 if($current_tags != "") {
467 $extern_link.= "&tags=". $current_tags;
469 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
470 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
473 $this->tmpl->assign('extern_link', $extern_link);
475 if(file_exists($thumb_path)) {
477 $info = getimagesize($thumb_path);
479 $this->tmpl->assign('description', $details['description']);
480 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
482 $this->tmpl->assign('width', $info[0]);
483 $this->tmpl->assign('height', $info[1]);
484 $this->tmpl->assign('ExifMadeOn', $meta_date);
485 $this->tmpl->assign('ExifMadeWith', $meta_make);
486 $this->tmpl->assign('ExifOrigResolution', $meta_res);
487 $this->tmpl->assign('ExifFileSize', $meta_size);
489 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
490 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
492 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
493 $this->tmpl->assign('current', $current);
496 $this->_error("Can't open file ". $thumb_path ."\n");
501 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
502 $this->tmpl->assign('prev_img', $previous_img);
506 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
507 $this->tmpl->assign('next_img', $next_img);
509 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
510 $this->tmpl->assign('photo_number', $i);
511 $this->tmpl->assign('photo_count', count($all_photos));
513 $this->tmpl->show("single_photo.tpl");
518 * all available tags and tag cloud
520 * this function outputs all available tags (time ordered)
521 * and in addition output them as tag cloud (tags which have
522 * many photos will appears more then others)
524 public function getAvailableTags()
530 $result = $this->db->db_query("
531 SELECT tag_id as id, count(tag_id) as quantity
541 while($row = $this->db->db_fetch_object($result)) {
542 $tags[$row['id']] = $row['quantity'];
545 // change these font sizes if you will
546 $max_size = 125; // max font size in %
547 $min_size = 75; // min font size in %
549 // get the largest and smallest array values
550 $max_qty = max(array_values($tags));
551 $min_qty = min(array_values($tags));
553 // find the range of values
554 $spread = $max_qty - $min_qty;
555 if (0 == $spread) { // we don't want to divide by zero
559 // determine the font-size increment
560 // this is the increase per tag quantity (times used)
561 $step = ($max_size - $min_size)/($spread);
563 // loop through our tag array
564 foreach ($tags as $key => $value) {
566 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
569 // calculate CSS font-size
570 // find the $value in excess of $min_qty
571 // multiply by the font-size increment ($size)
572 // and add the $min_size set above
573 $size = $min_size + (($value - $min_qty) * $step);
574 // uncomment if you want sizes in whole %:
577 if(isset($this->tags[$key])) {
578 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
583 $output = substr($output, 0, strlen($output)-2);
586 } // getAvailableTags()
589 * output all selected tags
591 * this function output all tags which have been selected
592 * by the user. the selected tags are stored in the
593 * session-variable $_SESSION['selected_tags']
595 public function getSelectedTags()
600 foreach($this->avail_tags as $tag)
602 // return all selected tags
603 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
604 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
608 $output = substr($output, 0, strlen($output)-2);
611 } // getSelectedTags()
614 * add tag to users session variable
616 * this function will add the specified to users current
617 * tag selection. if a date search has been made before
618 * it will be now cleared
620 public function addTag($tag)
622 if(!isset($_SESSION['selected_tags']))
623 $_SESSION['selected_tags'] = Array();
625 if(isset($_SESSION['searchfor']))
626 unset($_SESSION['searchfor']);
628 if(!in_array($tag, $_SESSION['selected_tags']))
629 array_push($_SESSION['selected_tags'], $tag);
634 * remove tag to users session variable
636 * this function removes the specified tag from
637 * users current tag selection
639 public function delTag($tag)
641 if(isset($_SESSION['searchfor']))
642 unset($_SESSION['searchfor']);
644 if(isset($_SESSION['selected_tags'])) {
645 $key = array_search($tag, $_SESSION['selected_tags']);
646 unset($_SESSION['selected_tags'][$key]);
647 sort($_SESSION['selected_tags']);
653 * reset tag selection
655 * if there is any tag selection, it will be
658 public function resetTags()
660 if(isset($_SESSION['selected_tags']))
661 unset($_SESSION['selected_tags']);
668 * if a specific photo was requested (external link)
669 * unset the session variable now
671 public function resetPhotoView()
673 if(isset($_SESSION['current_photo']))
674 unset($_SESSION['current_photo']);
676 } // resetPhotoView();
681 * if any tag search has taken place, reset
684 public function resetTagSearch()
686 if(isset($_SESSION['searchfor']))
687 unset($_SESSION['searchfor']);
689 } // resetTagSearch()
694 * if any date search has taken place, reset
697 public function resetDateSearch()
699 if(isset($_SESSION['from_date']))
700 unset($_SESSION['from_date']);
701 if(isset($_SESSION['to_date']))
702 unset($_SESSION['to_date']);
704 } // resetDateSearch();
707 * return all photo according selection
709 * this function returns all photos based on
710 * the tag-selection, tag- or date-search.
711 * the tag-search also has to take care of AND
712 * and OR conjunctions
714 public function getPhotoSelection()
716 $matched_photos = Array();
718 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
719 $from_date = $_SESSION['from_date'];
720 $to_date = $_SESSION['to_date'];
721 $additional_where_cond = "
722 p.time>='". $from_date ."'
724 p.time<='". $to_date ."'
728 if(isset($_SESSION['sort_order'])) {
729 $order_str = $this->get_sort_order();
732 /* return a search result */
733 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
735 SELECT DISTINCT pt1.photo_id
737 INNER JOIN photo_tags pt2
738 ON pt1.photo_id=pt2.photo_id
745 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
747 if(isset($additional_where_cond))
748 $query_str.= "AND ". $additional_where_cond ." ";
750 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
751 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
754 if(isset($order_str))
755 $query_str.= $order_str;
757 $result = $this->db->db_query($query_str);
758 while($row = $this->db->db_fetch_object($result)) {
759 array_push($matched_photos, $row['photo_id']);
761 return $matched_photos;
764 /* return according the selected tags */
765 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
767 foreach($_SESSION['selected_tags'] as $tag)
768 $selected.= $tag .",";
769 $selected = substr($selected, 0, strlen($selected)-1);
771 /* photo has to match at least on of the selected tags */
772 if($_SESSION['tag_condition'] == 'or') {
774 SELECT DISTINCT pt1.photo_id
776 INNER JOIN photo_tags pt2
777 ON pt1.photo_id=pt2.photo_id
782 WHERE pt1.tag_id IN (". $selected .")
784 if(isset($additional_where_cond))
785 $query_str.= "AND ". $additional_where_cond ." ";
787 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
788 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
791 if(isset($order_str))
792 $query_str.= $order_str;
794 /* photo has to match all selected tags */
795 elseif($_SESSION['tag_condition'] == 'and') {
797 if(count($_SESSION['selected_tags']) >= 32) {
798 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
799 print "evaluate your tag selection. Please remove some tags from your selection.\n";
803 /* Join together a table looking like
805 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
807 so the query can quickly return all images matching the
808 selected tags in an AND condition
813 SELECT DISTINCT pt1.photo_id
817 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
824 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
826 INNER JOIN photo_tags pt". ($i+2) ."
827 ON pt1.photo_id=pt". ($i+2) .".photo_id
834 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
835 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
837 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
840 if(isset($additional_where_cond))
841 $query_str.= "AND ". $additional_where_cond;
843 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
844 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
847 if(isset($order_str))
848 $query_str.= $order_str;
852 $result = $this->db->db_query($query_str);
853 while($row = $this->db->db_fetch_object($result)) {
854 array_push($matched_photos, $row['photo_id']);
856 return $matched_photos;
859 /* return all available photos */
861 SELECT DISTINCT photo_id
868 if(isset($additional_where_cond))
869 $query_str.= "WHERE ". $additional_where_cond ." ";
871 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
872 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
875 if(isset($order_str))
876 $query_str.= $order_str;
878 $result = $this->db->db_query($query_str);
879 while($row = $this->db->db_fetch_object($result)) {
880 array_push($matched_photos, $row['photo_id']);
882 return $matched_photos;
884 } // getPhotoSelection()
887 * control HTML ouput for photo index
889 * this function provides all the necessary information
890 * for the photo index template.
892 public function showPhotoIndex()
894 $photos = $this->getPhotoSelection();
896 $count = count($photos);
898 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
899 $anchor = $_SESSION['begin_with'];
901 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
907 elseif($this->cfg->rows_per_page > 0) {
909 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
914 $begin_with = $_SESSION['begin_with'];
916 // verify $begin_with - perhaps the thumbs-per-rows or
917 // rows-per-page variables have changed or the jump back
918 // from a photo wasn't exact - so calculate the real new
920 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
921 for($i = 0; $i <= $count; $i+=$multiplicator) {
922 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
929 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
935 $images[$rows] = Array();
936 $img_height[$rows] = Array();
937 $img_width[$rows] = Array();
938 $img_id[$rows] = Array();
939 $img_name[$rows] = Array();
940 $img_title = Array();
942 for($i = $begin_with; $i < $end_with; $i++) {
944 if(isset($photos[$i])) {
946 $images[$rows][$cols] = $photos[$i];
947 $img_id[$rows][$cols] = $i;
948 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
949 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
951 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
953 if(file_exists($thumb_path)) {
954 $info = getimagesize($thumb_path);
955 $img_width[$rows][$cols] = $info[0];
956 $img_height[$rows][$cols] = $info[1];
959 if($cols == $this->cfg->thumbs_per_row-1) {
962 $images[$rows] = Array();
963 $img_width[$rows] = Array();
964 $img_height[$rows] = Array();
972 // +1 for for smarty's selection iteration
975 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
976 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
978 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
979 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
980 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
983 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
984 $this->tmpl->assign('tag_result', 1);
987 /* do we have to display the page selector ? */
988 if($this->cfg->rows_per_page != 0) {
992 /* calculate the page switchers */
993 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
994 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
997 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
998 if($end_with < $count)
999 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1001 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
1002 $last_page = ceil($count / $photo_per_page);
1004 /* get the current selected page */
1005 if($begin_with == 0) {
1009 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1016 for($i = 1; $i <= $last_page; $i++) {
1018 if($current_page == $i)
1019 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1020 elseif($current_page-1 == $i || $current_page+1 == $i)
1021 $style = "style=\"font-size: 105%;\"";
1022 elseif(($current_page-5 >= $i) && ($i != 1) ||
1023 ($current_page+5 <= $i) && ($i != $last_page))
1024 $style = "style=\"font-size: 75%;\"";
1028 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1031 $select.= ">". $i ."</a> ";
1033 // until 9 pages we show the selector from 1-9
1034 if($last_page <= 9) {
1035 $page_select.= $select;
1038 if($i == 1 /* first page */ ||
1039 $i == $last_page /* last page */ ||
1040 $i == $current_page /* current page */ ||
1041 $i == ceil($last_page * 0.25) /* first quater */ ||
1042 $i == ceil($last_page * 0.5) /* half */ ||
1043 $i == ceil($last_page * 0.75) /* third quater */ ||
1044 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1045 (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 */ ||
1046 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1047 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1049 $page_select.= $select;
1057 $page_select.= "......... ";
1062 /* only show the page selector if we have more then one page */
1064 $this->tmpl->assign('page_selector', $page_select);
1068 $current_tags = $this->getCurrentTags();
1069 $extern_link = "index.php?mode=showpi";
1070 $rss_link = "index.php?mode=rss";
1071 if($current_tags != "") {
1072 $extern_link.= "&tags=". $current_tags;
1073 $rss_link.= "&tags=". $current_tags;
1075 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1076 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1077 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1080 $export_link = "index.php?mode=export";
1081 $slideshow_link = "index.php?mode=slideshow";
1083 $this->tmpl->assign('extern_link', $extern_link);
1084 $this->tmpl->assign('slideshow_link', $slideshow_link);
1085 $this->tmpl->assign('export_link', $export_link);
1086 $this->tmpl->assign('rss_link', $rss_link);
1087 $this->tmpl->assign('count', $count);
1088 $this->tmpl->assign('width', $this->cfg->thumb_width);
1089 $this->tmpl->assign('images', $images);
1090 $this->tmpl->assign('img_width', $img_width);
1091 $this->tmpl->assign('img_height', $img_height);
1092 $this->tmpl->assign('img_id', $img_id);
1093 $this->tmpl->assign('img_name', $img_name);
1094 $this->tmpl->assign('img_title', $img_title);
1095 $this->tmpl->assign('rows', $rows);
1096 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1098 $this->tmpl->show("photo_index.tpl");
1101 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1103 } // showPhotoIndex()
1106 * show credit template
1108 public function showCredits()
1110 $this->tmpl->assign('version', $this->cfg->version);
1111 $this->tmpl->assign('product', $this->cfg->product);
1112 $this->tmpl->assign('db_version', $this->dbver);
1113 $this->tmpl->show("credits.tpl");
1118 * create_thumbnails for the requested width
1120 * this function creates image thumbnails of $orig_image
1121 * stored as $thumb_image. It will check if the image is
1122 * in a supported format, if necessary rotate the image
1123 * (based on EXIF orientation meta headers) and re-sizing.
1125 public function create_thumbnail($orig_image, $thumb_image, $width)
1127 if(!file_exists($orig_image)) {
1131 $details = getimagesize($orig_image);
1133 /* check if original photo is a support image type */
1134 if(!$this->checkifImageSupported($details['mime']))
1137 $meta = $this->get_meta_informations($orig_image);
1142 switch($meta['Orientation']) {
1144 case 1: /* top, left */
1145 $rotate = 0; $flip = false; break;
1146 case 2: /* top, right */
1147 $rotate = 0; $flip = true; break;
1148 case 3: /* bottom, left */
1149 $rotate = 180; $flip = false; break;
1150 case 4: /* bottom, right */
1151 $rotate = 180; $flip = true; break;
1152 case 5: /* left side, top */
1153 $rotate = 90; $flip = true; break;
1154 case 6: /* right side, top */
1155 $rotate = 90; $flip = false; break;
1156 case 7: /* left side, bottom */
1157 $rotate = 270; $flip = true; break;
1158 case 8: /* right side, bottom */
1159 $rotate = 270; $flip = false; break;
1162 $src_img = @imagecreatefromjpeg($orig_image);
1165 print "Can't load image from ". $orig_image ."\n";
1169 /* grabs the height and width */
1170 $cur_width = imagesx($src_img);
1171 $cur_height = imagesy($src_img);
1173 // If requested width is more then the actual image width,
1174 // do not generate a thumbnail, instead safe the original
1175 // as thumbnail but with lower quality
1177 if($width >= $cur_width) {
1178 $result = imagejpeg($src_img, $thumb_image, 75);
1179 imagedestroy($src_img);
1183 // If the image will be rotate because EXIF orientation said so
1184 // 'virtually rotate' the image for further calculations
1185 if($rotate == 90 || $rotate == 270) {
1187 $cur_width = $cur_height;
1191 /* calculates aspect ratio */
1192 $aspect_ratio = $cur_height / $cur_width;
1195 if($aspect_ratio < 1) {
1197 $new_h = abs($new_w * $aspect_ratio);
1199 /* 'virtually' rotate the image and calculate it's ratio */
1200 $tmp_w = $cur_height;
1201 $tmp_h = $cur_width;
1202 /* now get the ratio from the 'rotated' image */
1203 $tmp_ratio = $tmp_h/$tmp_w;
1204 /* now calculate the new dimensions */
1206 $tmp_h = abs($tmp_w * $tmp_ratio);
1208 // now that we know, how high they photo should be, if it
1209 // gets rotated, use this high to scale the image
1211 $new_w = abs($new_h / $aspect_ratio);
1213 // If the image will be rotate because EXIF orientation said so
1214 // now 'virtually rotate' back the image for the image manipulation
1215 if($rotate == 90 || $rotate == 270) {
1222 /* creates new image of that size */
1223 $dst_img = imagecreatetruecolor($new_w, $new_h);
1225 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1227 /* copies resized portion of original image into new image */
1228 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1230 /* needs the image to be flipped horizontal? */
1234 for($x = 0; $x < $new_w; $x++) {
1235 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1240 $this->_debug("(ROTATE)");
1241 $dst_img = $this->rotateImage($dst_img, $rotate);
1244 /* write down new generated file */
1245 $result = imagejpeg($dst_img, $thumb_image, 75);
1247 /* free your mind */
1248 imagedestroy($dst_img);
1249 imagedestroy($src_img);
1251 if($result === false) {
1252 print "Can't write thumbnail ". $thumb_image ."\n";
1258 } // create_thumbnail()
1261 * return all exif meta data from the file
1263 public function get_meta_informations($file)
1265 return exif_read_data($file);
1267 } // get_meta_informations()
1270 * create phpfspot own sqlite database
1272 * this function creates phpfspots own sqlite database
1273 * if it does not exist yet. this own is used to store
1274 * some necessary informations (md5 sum's, ...).
1276 public function check_config_table()
1278 // if the config table doesn't exist yet, create it
1279 if(!$this->cfg_db->db_check_table_exists("images")) {
1280 $this->cfg_db->db_exec("
1281 CREATE TABLE images (
1282 img_idx int primary key,
1288 } // check_config_table
1291 * Generates a thumbnail from photo idx
1293 * This function will generate JPEG thumbnails from provided F-Spot photo
1296 * 1. Check if all thumbnail generations (width) are already in place and
1298 * 2. Check if the md5sum of the original file has changed
1299 * 3. Generate the thumbnails if needed
1301 public function gen_thumb($idx = 0, $force = 0)
1305 $resolutions = Array(
1306 $this->cfg->thumb_width,
1307 $this->cfg->photo_width,
1308 $this->cfg->mini_width,
1311 /* get details from F-Spot's database */
1312 $details = $this->get_photo_details($idx);
1314 /* calculate file MD5 sum */
1315 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1317 if(!file_exists($full_path)) {
1318 $this->_error("File ". $full_path ." does not exist\n");
1322 if(!is_readable($full_path)) {
1323 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1327 $file_md5 = md5_file($full_path);
1329 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1333 foreach($resolutions as $resolution) {
1335 $thumb_sub_path = substr($file_md5, 0, 2);
1336 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1338 if(!file_exists(dirname($thumb_path))) {
1339 mkdir(dirname($thumb_path), 0755);
1342 /* if the thumbnail file doesn't exist, create it */
1343 if(!file_exists($thumb_path)) {
1345 $this->_debug(" ". $resolution ."px");
1346 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1351 /* if the file hasn't changed there is no need to regen the thumb */
1352 elseif($file_md5 != $this->getMD5($idx) || $force) {
1354 $this->_debug(" ". $resolution ."px");
1355 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1363 $this->_debug(" already exist");
1366 /* set the new/changed MD5 sum for the current photo */
1368 $this->setMD5($idx, $file_md5);
1371 $this->_debug("\n");
1376 * returns stored md5 sum for a specific photo
1378 * this function queries the phpfspot database for a
1379 * stored MD5 checksum of the specified photo
1381 public function getMD5($idx)
1383 $result = $this->cfg_db->db_query("
1386 WHERE img_idx='". $idx ."'
1392 $img = $this->cfg_db->db_fetch_object($result);
1393 return $img['img_md5'];
1398 * set MD5 sum for the specific photo
1400 private function setMD5($idx, $md5)
1402 $result = $this->cfg_db->db_exec("
1403 REPLACE INTO images (img_idx, img_md5)
1404 VALUES ('". $idx ."', '". $md5 ."')
1410 * store current tag condition
1412 * this function stores the current tag condition
1413 * (AND or OR) in the users session variables
1415 public function setTagCondition($mode)
1417 $_SESSION['tag_condition'] = $mode;
1419 } // setTagCondition()
1422 * invoke tag & date search
1424 * this function will return all matching tags and store
1425 * them in the session variable selected_tags. furthermore
1426 * it also handles the date search.
1427 * getPhotoSelection() will then only return the matching
1430 public function startSearch($searchfor, $from = 0, $to = 0)
1434 $_SESSION['searchfor'] = $searchfor;
1436 $_SESSION['from_date'] = strtotime($from);
1438 unset($_SESSION['from_date']);
1440 $_SESSION['to_date'] = strtotime($to);
1442 unset($_SESSION['to_date']);
1444 if($searchfor != "") {
1445 /* new search, reset the current selected tags */
1446 $_SESSION['selected_tags'] = Array();
1447 foreach($this->avail_tags as $tag) {
1448 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1449 array_push($_SESSION['selected_tags'], $tag);
1456 * updates sort order in session variable
1458 * this function is invoked by RPC and will sort the requested
1459 * sort order in the session variable.
1461 public function updateSortOrder($order)
1463 if(isset($this->sort_orders[$order])) {
1464 $_SESSION['sort_order'] = $order;
1468 return "unkown error";
1470 } // updateSortOrder()
1475 * this function rotates the image according the
1478 private function rotateImage($img, $degrees)
1480 if(function_exists("imagerotate")) {
1481 $img = imagerotate($img, $degrees, 0);
1483 function imagerotate($src_img, $angle)
1485 $src_x = imagesx($src_img);
1486 $src_y = imagesy($src_img);
1487 if ($angle == 180) {
1491 elseif ($src_x <= $src_y) {
1495 elseif ($src_x >= $src_y) {
1500 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1501 imagealphablending($rotate, false);
1506 for ($y = 0; $y < ($src_y); $y++) {
1507 for ($x = 0; $x < ($src_x); $x++) {
1508 $color = imagecolorat($src_img, $x, $y);
1509 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1515 for ($y = 0; $y < ($src_y); $y++) {
1516 for ($x = 0; $x < ($src_x); $x++) {
1517 $color = imagecolorat($src_img, $x, $y);
1518 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1524 for ($y = 0; $y < ($src_y); $y++) {
1525 for ($x = 0; $x < ($src_x); $x++) {
1526 $color = imagecolorat($src_img, $x, $y);
1527 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1541 $img = imagerotate($img, $degrees);
1550 * return all assigned tags for the specified photo
1552 private function get_photo_tags($idx)
1554 $result = $this->db->db_query("
1557 INNER JOIN photo_tags pt
1559 WHERE pt.photo_id='". $idx ."'
1564 while($row = $this->db->db_fetch_object($result))
1565 $tags[$row['id']] = $row['name'];
1569 } // get_photo_tags()
1572 * create on-the-fly images with text within
1574 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1576 if (strlen($color) != 6)
1579 $int = hexdec($color);
1580 $h = imagefontheight($font);
1581 $fw = imagefontwidth($font);
1582 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1583 $lines = count($txt);
1584 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1585 $bg = imagecolorallocate($im, 255, 255, 255);
1586 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1589 foreach ($txt as $text) {
1590 $x = (($w - ($fw * strlen($text))) / 2);
1591 imagestring($im, $font, $x, $y, $text, $color);
1592 $y += ($h + $space);
1595 Header("Content-type: image/png");
1598 } // showTextImage()
1601 * check if all requirements are met
1603 private function checkRequirements()
1605 if(!function_exists("imagecreatefromjpeg")) {
1606 print "PHP GD library extension is missing<br />\n";
1610 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1611 print "PHP SQLite3 library extension is missing<br />\n";
1615 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1616 ini_set('track_errors', 1);
1617 @include_once 'HTML/AJAX/Server.php';
1618 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1619 print "PEAR HTML_AJAX package is missing<br />\n";
1622 @include_once 'Calendar/Calendar.php';
1623 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1624 print "PEAR Calendar package is missing<br />\n";
1627 ini_restore('track_errors');
1634 } // checkRequirements()
1636 private function _debug($text)
1638 if($this->fromcmd) {
1645 * check if specified MIME type is supported
1647 public function checkifImageSupported($mime)
1649 if(in_array($mime, Array("image/jpeg")))
1654 } // checkifImageSupported()
1656 public function _error($text)
1658 switch($this->cfg->logging) {
1660 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1667 error_log($text, 3, $his->cfg->log_file);
1674 * output calendard input fields
1676 private function get_calendar($mode)
1678 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1679 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1680 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1682 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1683 if(!isset($_SESSION[$mode .'_date']))
1684 $output.= " disabled=\"disabled\"";
1686 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1687 if(!isset($_SESSION[$mode .'_date']))
1688 $output.= " disabled=\"disabled\"";
1690 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1691 if(!isset($_SESSION[$mode .'_date']))
1692 $output.= " disabled=\"disabled\"";
1700 * output calendar matrix
1702 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1704 if (!isset($year)) $year = date('Y');
1705 if (!isset($month)) $month = date('m');
1706 if (!isset($day)) $day = date('d');
1711 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1712 require_once CALENDAR_ROOT.'Day.php';
1715 $month = new Calendar_Month_Weekdays($year,$month);
1718 $prevStamp = $month->prevMonth(true);
1719 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1720 $nextStamp = $month->nextMonth(true);
1721 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1723 $selectedDays = array (
1724 new Calendar_Day($year,$month,$day),
1725 new Calendar_Day($year,12,25),
1728 // Build the days in the month
1729 $month->build($selectedDays);
1731 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1732 $this->tmpl->assign('prev_month', $prev);
1733 $this->tmpl->assign('next_month', $next);
1735 while ( $day = $month->fetch() ) {
1737 if(!isset($matrix[$rows]))
1738 $matrix[$rows] = Array();
1742 $dayStamp = $day->thisDay(true);
1743 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1745 // isFirst() to find start of week
1746 if ( $day->isFirst() )
1749 if ( $day->isSelected() ) {
1750 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1751 } else if ( $day->isEmpty() ) {
1752 $string.= "<td> </td>\n";
1754 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1757 // isLast() to find end of week
1758 if ( $day->isLast() )
1759 $string.= "</tr>\n";
1761 $matrix[$rows][$cols] = $string;
1771 $this->tmpl->assign('matrix', $matrix);
1772 $this->tmpl->assign('rows', $rows);
1773 $this->tmpl->show("calendar.tpl");
1775 } // get_calendar_matrix()
1778 * output export page
1780 public function getExport($mode)
1782 $pictures = $this->getPhotoSelection();
1783 $current_tags = $this->getCurrentTags();
1785 foreach($pictures as $picture) {
1787 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1788 if($current_tags != "") {
1789 $orig_url.= "&tags=". $current_tags;
1791 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1792 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1795 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1800 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1801 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1805 // "[%pictureurl% %thumbnailurl%]"
1806 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1809 case 'MoinMoinList':
1810 // " * [%pictureurl% %thumbnailurl%]"
1811 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1822 public function getRSSFeed()
1824 Header("Content-type: text/xml; charset=utf-8");
1825 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1828 xmlns:media="http://search.yahoo.com/mrss/"
1829 xmlns:dc="http://purl.org/dc/elements/1.1/"
1832 <title>phpfspot</title>
1833 <description>phpfspot RSS feed</description>
1834 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1835 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1836 <generator>phpfspot</generator>
1839 $pictures = $this->getPhotoSelection();
1840 $current_tags = $this->getCurrentTags();
1842 foreach($pictures as $picture) {
1844 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1845 if($current_tags != "") {
1846 $orig_url.= "&tags=". $current_tags;
1848 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1849 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1852 $details = $this->get_photo_details($picture);
1854 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1855 $thumb_html = htmlspecialchars("
1856 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1858 ". $details['description']);
1860 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1861 $meta = $this->get_meta_informations($orig_path);
1862 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1866 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1867 <link><?php print htmlspecialchars($orig_url); ?></link>
1868 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1869 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1871 <?php print $thumb_html; ?>
1873 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1888 * return all selected tags as one string
1890 private function getCurrentTags()
1893 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1894 foreach($_SESSION['selected_tags'] as $tag)
1895 $current_tags.= $tag .",";
1896 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1898 return $current_tags;
1900 } // getCurrentTags()
1903 * return the current photo
1905 public function getCurrentPhoto()
1907 if(isset($_SESSION['current_photo'])) {
1908 print $_SESSION['current_photo'];
1910 } // getCurrentPhoto()
1913 * tells the client browser what to do
1915 * this function is getting called via AJAX by the
1916 * client browsers. it will tell them what they have
1917 * to do next. This is necessary for directly jumping
1918 * into photo index or single photo view when the are
1919 * requested with specific URLs
1921 public function whatToDo()
1923 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1924 return "show_photo";
1926 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1927 return "showpi_tags";
1929 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1933 return "nothing special";
1938 * return the current process-user
1940 private function getuid()
1942 if($uid = posix_getuid()) {
1943 if($user = posix_getpwuid($uid)) {
1944 return $user['name'];
1953 * returns a select-dropdown box to select photo index sort parameters
1955 public function smarty_sort_select_list($params, &$smarty)
1959 foreach($this->sort_orders as $key => $value) {
1960 $output.= "<option value=\"". $key ."\"";
1961 if($key == $_SESSION['sort_order']) {
1962 $output.= " selected=\"selected\"";
1964 $output.= ">". $value ."</option>";
1969 } // smarty_sort_select_list()
1972 * returns the currently selected sort order
1974 private function get_sort_order()
1976 switch($_SESSION['sort_order']) {
1978 return " ORDER BY p.time ASC";
1981 return " ORDER BY p.time DESC";
1984 if($this->dbver < 9) {
1985 return " ORDER BY p.name ASC";
1988 return " ORDER BY basename(p.uri) ASC";
1992 if($this->dbver < 9) {
1993 return " ORDER BY p.name DESC";
1996 return " ORDER BY basename(p.uri) DESC";
2001 } // get_sort_order()
2004 * return the next to be shown slide show image
2006 * this function returns the URL of the next image
2007 * in the slideshow sequence.
2009 public function getNextSlideShowImage()
2011 $all_photos = $this->getPhotoSelection();
2013 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2014 $_SESSION['slideshow_img'] = 0;
2016 $_SESSION['slideshow_img']++;
2018 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2020 } // getNextSlideShowImage()
2023 * return the previous to be shown slide show image
2025 * this function returns the URL of the previous image
2026 * in the slideshow sequence.
2028 public function getPrevSlideShowImage()
2030 $all_photos = $this->getPhotoSelection();
2032 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2033 $_SESSION['slideshow_img'] = 0;
2035 $_SESSION['slideshow_img']--;
2037 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2039 } // getPrevSlideShowImage()
2041 public function resetSlideShow()
2043 if(isset($_SESSION['slideshow_img']))
2044 unset($_SESSION['slideshow_img']);
2045 } // resetSlideShow()
2050 * this function will get all photos from the fspot
2051 * database and randomly return ONE entry
2053 * saddly there is yet no sqlite3 function which returns
2054 * the bulk result in array, so we have to fill up our
2057 public function get_random_photo()
2061 $result = $this->db->db_query("
2066 while($row = $this->db->db_fetch_object($result)) {
2067 array_push($all, $row['id']);
2070 return $all[array_rand($all)];
2072 } // get_random_photo()
2075 * validates provided date
2077 * this function validates if the provided date
2078 * contains a valid date and will return true
2081 public function isValidDate($date_str)
2083 $timestamp = strtotime($date_str);
2085 if(is_numeric($timestamp))
2093 * timestamp to string conversion
2095 private function ts2str($timestamp)
2097 return strftime("%Y-%m-%d", $timestamp);
2100 private function extractTags($tags_str)
2102 $not_validated = split(',', $_GET['tags']);
2103 $validated = array();
2105 foreach($not_validated as $tag) {
2106 if(is_numeric($tag))
2107 array_push($validated, $tag);
2115 * returns the full path to a thumbnail
2117 public function get_thumb_path($width, $photo)
2119 $md5 = $this->getMD5($photo);
2120 $sub_path = substr($md5, 0, 2);
2121 return $this->cfg->thumb_path
2129 } // get_thumb_path()
2132 * returns server's virtual host name
2134 private function get_server_name()
2136 return $_SERVER['SERVER_NAME'];
2137 } // get_server_name()
2140 * returns type of webprotocol which is
2143 private function get_web_protocol()
2145 if(!isset($_SERVER['HTTPS']))
2149 } // get_web_protocol()
2152 * return url to this phpfspot installation
2154 private function get_phpfspot_url()
2156 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2157 } // get_phpfspot_url()
2160 * check file exists and is readable
2162 * returns true, if everything is ok, otherwise false
2163 * if $silent is not set, this function will output and
2166 private function check_readable($file, $silent = null)
2168 if(!file_exists($file)) {
2170 print "File \"". $file ."\" does not exist.\n";
2174 if(!is_readable($file)) {
2176 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2182 } // check_readable()
2185 * check if all needed indices are present
2187 * this function checks, if some needed indices are already
2188 * present, or if not, create them on the fly. they are
2189 * necessary to speed up some queries like that one look for
2190 * all tags, when show_tags is specified in the configuration.
2192 private function checkDbIndices()
2194 $result = $this->db->db_exec("
2195 CREATE INDEX IF NOT EXISTS
2202 } // checkDbIndices()
2205 * retrive F-Spot database version
2207 * this function will return the F-Spot database version number
2208 * It is stored within the sqlite3 database in the table meta
2210 public function getFspotDBVersion()
2212 if($result = $this->db->db_fetchSingleRow("
2213 SELECT data as version
2216 name LIKE 'F-Spot Database Version'
2218 return $result['version'];
2222 } // getFspotDBVersion()
2225 * parse the provided URI and will returned the
2228 public function parse_uri($uri, $mode)
2230 if(($components = parse_url($uri)) !== false) {
2234 return basename($components['path']);
2237 return dirname($components['path']);
2240 return $components['path'];