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'] ." 00:00:00");
170 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
171 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
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()
526 /* retrive tags from database */
531 $result = $this->db->db_query("
532 SELECT tag_id as id, count(tag_id) as quantity
542 while($row = $this->db->db_fetch_object($result)) {
543 $tags[$row['id']] = $row['quantity'];
546 // change these font sizes if you will
547 $max_size = 125; // max font size in %
548 $min_size = 75; // min font size in %
550 // get the largest and smallest array values
551 $max_qty = max(array_values($tags));
552 $min_qty = min(array_values($tags));
554 // find the range of values
555 $spread = $max_qty - $min_qty;
556 if (0 == $spread) { // we don't want to divide by zero
560 // determine the font-size increment
561 // this is the increase per tag quantity (times used)
562 $step = ($max_size - $min_size)/($spread);
564 // loop through our tag array
565 foreach ($tags as $key => $value) {
567 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
570 // calculate CSS font-size
571 // find the $value in excess of $min_qty
572 // multiply by the font-size increment ($size)
573 // and add the $min_size set above
574 $size = $min_size + (($value - $min_qty) * $step);
575 // uncomment if you want sizes in whole %:
578 if(isset($this->tags[$key])) {
579 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
584 $output = substr($output, 0, strlen($output)-2);
587 } // getAvailableTags()
590 * output all selected tags
592 * this function output all tags which have been selected
593 * by the user. the selected tags are stored in the
594 * session-variable $_SESSION['selected_tags']
596 public function getSelectedTags()
598 /* retrive tags from database */
603 foreach($this->avail_tags as $tag)
605 // return all selected tags
606 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
607 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
612 $output = substr($output, 0, strlen($output)-2);
616 return "no tags selected";
619 } // getSelectedTags()
622 * add tag to users session variable
624 * this function will add the specified to users current
625 * tag selection. if a date search has been made before
626 * it will be now cleared
628 public function addTag($tag)
630 if(!isset($_SESSION['selected_tags']))
631 $_SESSION['selected_tags'] = Array();
633 if(isset($_SESSION['searchfor']))
634 unset($_SESSION['searchfor']);
636 if(!in_array($tag, $_SESSION['selected_tags']))
637 array_push($_SESSION['selected_tags'], $tag);
642 * remove tag to users session variable
644 * this function removes the specified tag from
645 * users current tag selection
647 public function delTag($tag)
649 if(isset($_SESSION['searchfor']))
650 unset($_SESSION['searchfor']);
652 if(isset($_SESSION['selected_tags'])) {
653 $key = array_search($tag, $_SESSION['selected_tags']);
654 unset($_SESSION['selected_tags'][$key]);
655 sort($_SESSION['selected_tags']);
661 * reset tag selection
663 * if there is any tag selection, it will be
666 public function resetTags()
668 if(isset($_SESSION['selected_tags']))
669 unset($_SESSION['selected_tags']);
676 * if a specific photo was requested (external link)
677 * unset the session variable now
679 public function resetPhotoView()
681 if(isset($_SESSION['current_photo']))
682 unset($_SESSION['current_photo']);
684 } // resetPhotoView();
689 * if any tag search has taken place, reset
692 public function resetTagSearch()
694 if(isset($_SESSION['searchfor']))
695 unset($_SESSION['searchfor']);
697 } // resetTagSearch()
702 * if any date search has taken place, reset
705 public function resetDateSearch()
707 if(isset($_SESSION['from_date']))
708 unset($_SESSION['from_date']);
709 if(isset($_SESSION['to_date']))
710 unset($_SESSION['to_date']);
712 } // resetDateSearch();
715 * return all photo according selection
717 * this function returns all photos based on
718 * the tag-selection, tag- or date-search.
719 * the tag-search also has to take care of AND
720 * and OR conjunctions
722 public function getPhotoSelection()
724 $matched_photos = Array();
726 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
727 $from_date = $_SESSION['from_date'];
728 $to_date = $_SESSION['to_date'];
729 $additional_where_cond = "
730 p.time>='". $from_date ."'
732 p.time<='". $to_date ."'
736 if(isset($_SESSION['sort_order'])) {
737 $order_str = $this->get_sort_order();
740 /* return a search result */
741 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
743 SELECT DISTINCT pt1.photo_id
745 INNER JOIN photo_tags pt2
746 ON pt1.photo_id=pt2.photo_id
753 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
755 if(isset($additional_where_cond))
756 $query_str.= "AND ". $additional_where_cond ." ";
758 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
759 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
762 if(isset($order_str))
763 $query_str.= $order_str;
765 $result = $this->db->db_query($query_str);
766 while($row = $this->db->db_fetch_object($result)) {
767 array_push($matched_photos, $row['photo_id']);
769 return $matched_photos;
772 /* return according the selected tags */
773 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
775 foreach($_SESSION['selected_tags'] as $tag)
776 $selected.= $tag .",";
777 $selected = substr($selected, 0, strlen($selected)-1);
779 /* photo has to match at least on of the selected tags */
780 if($_SESSION['tag_condition'] == 'or') {
782 SELECT DISTINCT pt1.photo_id
784 INNER JOIN photo_tags pt2
785 ON pt1.photo_id=pt2.photo_id
790 WHERE pt1.tag_id IN (". $selected .")
792 if(isset($additional_where_cond))
793 $query_str.= "AND ". $additional_where_cond ." ";
795 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
796 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
799 if(isset($order_str))
800 $query_str.= $order_str;
802 /* photo has to match all selected tags */
803 elseif($_SESSION['tag_condition'] == 'and') {
805 if(count($_SESSION['selected_tags']) >= 32) {
806 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
807 print "evaluate your tag selection. Please remove some tags from your selection.\n";
811 /* Join together a table looking like
813 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
815 so the query can quickly return all images matching the
816 selected tags in an AND condition
821 SELECT DISTINCT pt1.photo_id
825 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
832 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
834 INNER JOIN photo_tags pt". ($i+2) ."
835 ON pt1.photo_id=pt". ($i+2) .".photo_id
842 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
843 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
845 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
848 if(isset($additional_where_cond))
849 $query_str.= "AND ". $additional_where_cond;
851 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
852 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
855 if(isset($order_str))
856 $query_str.= $order_str;
860 $result = $this->db->db_query($query_str);
861 while($row = $this->db->db_fetch_object($result)) {
862 array_push($matched_photos, $row['photo_id']);
864 return $matched_photos;
867 /* return all available photos */
871 LEFT JOIN photo_tags pt
877 if(isset($additional_where_cond))
878 $query_str.= "WHERE ". $additional_where_cond ." ";
880 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
881 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
884 if(isset($order_str))
885 $query_str.= $order_str;
887 $result = $this->db->db_query($query_str);
888 while($row = $this->db->db_fetch_object($result)) {
889 array_push($matched_photos, $row['id']);
891 return $matched_photos;
893 } // getPhotoSelection()
896 * control HTML ouput for photo index
898 * this function provides all the necessary information
899 * for the photo index template.
901 public function showPhotoIndex()
903 $photos = $this->getPhotoSelection();
905 $count = count($photos);
907 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
908 $anchor = $_SESSION['begin_with'];
910 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
916 elseif($this->cfg->rows_per_page > 0) {
918 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
923 $begin_with = $_SESSION['begin_with'];
925 // verify $begin_with - perhaps the thumbs-per-rows or
926 // rows-per-page variables have changed or the jump back
927 // from a photo wasn't exact - so calculate the real new
929 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
930 for($i = 0; $i <= $count; $i+=$multiplicator) {
931 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
938 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
944 $images[$rows] = Array();
945 $img_height[$rows] = Array();
946 $img_width[$rows] = Array();
947 $img_id[$rows] = Array();
948 $img_name[$rows] = Array();
949 $img_title = Array();
951 for($i = $begin_with; $i < $end_with; $i++) {
953 if(isset($photos[$i])) {
955 $images[$rows][$cols] = $photos[$i];
956 $img_id[$rows][$cols] = $i;
957 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
958 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
960 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
962 if(file_exists($thumb_path)) {
963 $info = getimagesize($thumb_path);
964 $img_width[$rows][$cols] = $info[0];
965 $img_height[$rows][$cols] = $info[1];
968 if($cols == $this->cfg->thumbs_per_row-1) {
971 $images[$rows] = Array();
972 $img_width[$rows] = Array();
973 $img_height[$rows] = Array();
981 // +1 for for smarty's selection iteration
984 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
985 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
987 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
988 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
989 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
992 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
993 $this->tmpl->assign('tag_result', 1);
996 /* do we have to display the page selector ? */
997 if($this->cfg->rows_per_page != 0) {
1001 /* calculate the page switchers */
1002 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
1003 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
1005 if($begin_with != 0)
1006 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1007 if($end_with < $count)
1008 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1010 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
1011 $last_page = ceil($count / $photo_per_page);
1013 /* get the current selected page */
1014 if($begin_with == 0) {
1018 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1025 for($i = 1; $i <= $last_page; $i++) {
1027 if($current_page == $i)
1028 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1029 elseif($current_page-1 == $i || $current_page+1 == $i)
1030 $style = "style=\"font-size: 105%;\"";
1031 elseif(($current_page-5 >= $i) && ($i != 1) ||
1032 ($current_page+5 <= $i) && ($i != $last_page))
1033 $style = "style=\"font-size: 75%;\"";
1037 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1040 $select.= ">". $i ."</a> ";
1042 // until 9 pages we show the selector from 1-9
1043 if($last_page <= 9) {
1044 $page_select.= $select;
1047 if($i == 1 /* first page */ ||
1048 $i == $last_page /* last page */ ||
1049 $i == $current_page /* current page */ ||
1050 $i == ceil($last_page * 0.25) /* first quater */ ||
1051 $i == ceil($last_page * 0.5) /* half */ ||
1052 $i == ceil($last_page * 0.75) /* third quater */ ||
1053 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1054 (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 */ ||
1055 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1056 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1058 $page_select.= $select;
1066 $page_select.= "......... ";
1071 /* only show the page selector if we have more then one page */
1073 $this->tmpl->assign('page_selector', $page_select);
1077 $current_tags = $this->getCurrentTags();
1078 $extern_link = "index.php?mode=showpi";
1079 $rss_link = "index.php?mode=rss";
1080 if($current_tags != "") {
1081 $extern_link.= "&tags=". $current_tags;
1082 $rss_link.= "&tags=". $current_tags;
1084 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1085 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1086 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1089 $export_link = "index.php?mode=export";
1090 $slideshow_link = "index.php?mode=slideshow";
1092 $this->tmpl->assign('extern_link', $extern_link);
1093 $this->tmpl->assign('slideshow_link', $slideshow_link);
1094 $this->tmpl->assign('export_link', $export_link);
1095 $this->tmpl->assign('rss_link', $rss_link);
1096 $this->tmpl->assign('count', $count);
1097 $this->tmpl->assign('width', $this->cfg->thumb_width);
1098 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width+20);
1099 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height);
1100 $this->tmpl->assign('images', $images);
1101 $this->tmpl->assign('img_width', $img_width);
1102 $this->tmpl->assign('img_height', $img_height);
1103 $this->tmpl->assign('img_id', $img_id);
1104 $this->tmpl->assign('img_name', $img_name);
1105 $this->tmpl->assign('img_title', $img_title);
1106 $this->tmpl->assign('rows', $rows);
1107 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1109 $this->tmpl->show("photo_index.tpl");
1112 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1114 } // showPhotoIndex()
1117 * show credit template
1119 public function showCredits()
1121 $this->tmpl->assign('version', $this->cfg->version);
1122 $this->tmpl->assign('product', $this->cfg->product);
1123 $this->tmpl->assign('db_version', $this->dbver);
1124 $this->tmpl->show("credits.tpl");
1129 * create_thumbnails for the requested width
1131 * this function creates image thumbnails of $orig_image
1132 * stored as $thumb_image. It will check if the image is
1133 * in a supported format, if necessary rotate the image
1134 * (based on EXIF orientation meta headers) and re-sizing.
1136 public function create_thumbnail($orig_image, $thumb_image, $width)
1138 if(!file_exists($orig_image)) {
1142 $details = getimagesize($orig_image);
1144 /* check if original photo is a support image type */
1145 if(!$this->checkifImageSupported($details['mime']))
1148 $meta = $this->get_meta_informations($orig_image);
1153 switch($meta['Orientation']) {
1155 case 1: /* top, left */
1156 $rotate = 0; $flip = false; break;
1157 case 2: /* top, right */
1158 $rotate = 0; $flip = true; break;
1159 case 3: /* bottom, left */
1160 $rotate = 180; $flip = false; break;
1161 case 4: /* bottom, right */
1162 $rotate = 180; $flip = true; break;
1163 case 5: /* left side, top */
1164 $rotate = 90; $flip = true; break;
1165 case 6: /* right side, top */
1166 $rotate = 90; $flip = false; break;
1167 case 7: /* left side, bottom */
1168 $rotate = 270; $flip = true; break;
1169 case 8: /* right side, bottom */
1170 $rotate = 270; $flip = false; break;
1173 $src_img = @imagecreatefromjpeg($orig_image);
1176 print "Can't load image from ". $orig_image ."\n";
1180 /* grabs the height and width */
1181 $cur_width = imagesx($src_img);
1182 $cur_height = imagesy($src_img);
1184 // If requested width is more then the actual image width,
1185 // do not generate a thumbnail, instead safe the original
1186 // as thumbnail but with lower quality. But if the image
1187 // is to heigh too, then we still have to resize it.
1188 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1189 $result = imagejpeg($src_img, $thumb_image, 75);
1190 imagedestroy($src_img);
1194 // If the image will be rotate because EXIF orientation said so
1195 // 'virtually rotate' the image for further calculations
1196 if($rotate == 90 || $rotate == 270) {
1198 $cur_width = $cur_height;
1202 /* calculates aspect ratio */
1203 $aspect_ratio = $cur_height / $cur_width;
1206 if($aspect_ratio < 1) {
1208 $new_h = abs($new_w * $aspect_ratio);
1210 /* 'virtually' rotate the image and calculate it's ratio */
1211 $tmp_w = $cur_height;
1212 $tmp_h = $cur_width;
1213 /* now get the ratio from the 'rotated' image */
1214 $tmp_ratio = $tmp_h/$tmp_w;
1215 /* now calculate the new dimensions */
1217 $tmp_h = abs($tmp_w * $tmp_ratio);
1219 // now that we know, how high they photo should be, if it
1220 // gets rotated, use this high to scale the image
1222 $new_w = abs($new_h / $aspect_ratio);
1224 // If the image will be rotate because EXIF orientation said so
1225 // now 'virtually rotate' back the image for the image manipulation
1226 if($rotate == 90 || $rotate == 270) {
1233 /* creates new image of that size */
1234 $dst_img = imagecreatetruecolor($new_w, $new_h);
1236 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1238 /* copies resized portion of original image into new image */
1239 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1241 /* needs the image to be flipped horizontal? */
1245 for($x = 0; $x < $new_w; $x++) {
1246 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1251 $this->_debug("(ROTATE)");
1252 $dst_img = $this->rotateImage($dst_img, $rotate);
1255 /* write down new generated file */
1256 $result = imagejpeg($dst_img, $thumb_image, 75);
1258 /* free your mind */
1259 imagedestroy($dst_img);
1260 imagedestroy($src_img);
1262 if($result === false) {
1263 print "Can't write thumbnail ". $thumb_image ."\n";
1269 } // create_thumbnail()
1272 * return all exif meta data from the file
1274 public function get_meta_informations($file)
1276 return exif_read_data($file);
1278 } // get_meta_informations()
1281 * create phpfspot own sqlite database
1283 * this function creates phpfspots own sqlite database
1284 * if it does not exist yet. this own is used to store
1285 * some necessary informations (md5 sum's, ...).
1287 public function check_config_table()
1289 // if the config table doesn't exist yet, create it
1290 if(!$this->cfg_db->db_check_table_exists("images")) {
1291 $this->cfg_db->db_exec("
1292 CREATE TABLE images (
1293 img_idx int primary key,
1299 } // check_config_table
1302 * Generates a thumbnail from photo idx
1304 * This function will generate JPEG thumbnails from provided F-Spot photo
1307 * 1. Check if all thumbnail generations (width) are already in place and
1309 * 2. Check if the md5sum of the original file has changed
1310 * 3. Generate the thumbnails if needed
1312 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1316 $resolutions = Array(
1317 $this->cfg->thumb_width,
1318 $this->cfg->photo_width,
1319 $this->cfg->mini_width,
1322 /* get details from F-Spot's database */
1323 $details = $this->get_photo_details($idx);
1325 /* calculate file MD5 sum */
1326 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1328 if(!file_exists($full_path)) {
1329 $this->_error("File ". $full_path ." does not exist\n");
1333 if(!is_readable($full_path)) {
1334 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1338 $file_md5 = md5_file($full_path);
1340 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1344 foreach($resolutions as $resolution) {
1346 $generate_it = false;
1348 $thumb_sub_path = substr($file_md5, 0, 2);
1349 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1351 if(!file_exists(dirname($thumb_path))) {
1352 mkdir(dirname($thumb_path), 0755);
1355 /* if the thumbnail file doesn't exist, create it */
1356 if(!file_exists($thumb_path)) {
1357 $generate_it = true;
1359 /* if the file hasn't changed there is no need to regen the thumb */
1360 elseif($file_md5 != $this->getMD5($idx) || $force) {
1361 $generate_it = true;
1364 if($generate_it || $overwrite) {
1366 $this->_debug(" ". $resolution ."px");
1367 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1375 $this->_debug(" already exist");
1378 /* set the new/changed MD5 sum for the current photo */
1380 $this->setMD5($idx, $file_md5);
1383 $this->_debug("\n");
1388 * returns stored md5 sum for a specific photo
1390 * this function queries the phpfspot database for a
1391 * stored MD5 checksum of the specified photo
1393 public function getMD5($idx)
1395 $result = $this->cfg_db->db_query("
1398 WHERE img_idx='". $idx ."'
1404 $img = $this->cfg_db->db_fetch_object($result);
1405 return $img['img_md5'];
1410 * set MD5 sum for the specific photo
1412 private function setMD5($idx, $md5)
1414 $result = $this->cfg_db->db_exec("
1415 REPLACE INTO images (img_idx, img_md5)
1416 VALUES ('". $idx ."', '". $md5 ."')
1422 * store current tag condition
1424 * this function stores the current tag condition
1425 * (AND or OR) in the users session variables
1427 public function setTagCondition($mode)
1429 $_SESSION['tag_condition'] = $mode;
1431 } // setTagCondition()
1434 * invoke tag & date search
1436 * this function will return all matching tags and store
1437 * them in the session variable selected_tags. furthermore
1438 * it also handles the date search.
1439 * getPhotoSelection() will then only return the matching
1442 public function startSearch($searchfor, $from = 0, $to = 0)
1446 $_SESSION['searchfor'] = $searchfor;
1449 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1451 unset($_SESSION['from_date']);
1454 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1456 unset($_SESSION['to_date']);
1458 if($searchfor != "") {
1459 /* new search, reset the current selected tags */
1460 $_SESSION['selected_tags'] = Array();
1461 foreach($this->avail_tags as $tag) {
1462 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1463 array_push($_SESSION['selected_tags'], $tag);
1470 * updates sort order in session variable
1472 * this function is invoked by RPC and will sort the requested
1473 * sort order in the session variable.
1475 public function updateSortOrder($order)
1477 if(isset($this->sort_orders[$order])) {
1478 $_SESSION['sort_order'] = $order;
1482 return "unkown error";
1484 } // updateSortOrder()
1489 * this function rotates the image according the
1492 private function rotateImage($img, $degrees)
1494 if(function_exists("imagerotate")) {
1495 $img = imagerotate($img, $degrees, 0);
1497 function imagerotate($src_img, $angle)
1499 $src_x = imagesx($src_img);
1500 $src_y = imagesy($src_img);
1501 if ($angle == 180) {
1505 elseif ($src_x <= $src_y) {
1509 elseif ($src_x >= $src_y) {
1514 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1515 imagealphablending($rotate, false);
1520 for ($y = 0; $y < ($src_y); $y++) {
1521 for ($x = 0; $x < ($src_x); $x++) {
1522 $color = imagecolorat($src_img, $x, $y);
1523 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1529 for ($y = 0; $y < ($src_y); $y++) {
1530 for ($x = 0; $x < ($src_x); $x++) {
1531 $color = imagecolorat($src_img, $x, $y);
1532 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1538 for ($y = 0; $y < ($src_y); $y++) {
1539 for ($x = 0; $x < ($src_x); $x++) {
1540 $color = imagecolorat($src_img, $x, $y);
1541 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1555 $img = imagerotate($img, $degrees);
1564 * return all assigned tags for the specified photo
1566 private function get_photo_tags($idx)
1568 $result = $this->db->db_query("
1571 INNER JOIN photo_tags pt
1573 WHERE pt.photo_id='". $idx ."'
1578 while($row = $this->db->db_fetch_object($result))
1579 $tags[$row['id']] = $row['name'];
1583 } // get_photo_tags()
1586 * create on-the-fly images with text within
1588 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1590 if (strlen($color) != 6)
1593 $int = hexdec($color);
1594 $h = imagefontheight($font);
1595 $fw = imagefontwidth($font);
1596 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1597 $lines = count($txt);
1598 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1599 $bg = imagecolorallocate($im, 255, 255, 255);
1600 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1603 foreach ($txt as $text) {
1604 $x = (($w - ($fw * strlen($text))) / 2);
1605 imagestring($im, $font, $x, $y, $text, $color);
1606 $y += ($h + $space);
1609 Header("Content-type: image/png");
1612 } // showTextImage()
1615 * check if all requirements are met
1617 private function checkRequirements()
1619 if(!function_exists("imagecreatefromjpeg")) {
1620 print "PHP GD library extension is missing<br />\n";
1624 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1625 print "PHP SQLite3 library extension is missing<br />\n";
1629 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1630 ini_set('track_errors', 1);
1631 @include_once 'HTML/AJAX/Server.php';
1632 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1633 print "PEAR HTML_AJAX package is missing<br />\n";
1636 @include_once 'Calendar/Calendar.php';
1637 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1638 print "PEAR Calendar package is missing<br />\n";
1641 @include_once 'Console/Getopt.php';
1642 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1643 print "PEAR Console_Getopt package is missing<br />\n";
1646 ini_restore('track_errors');
1653 } // checkRequirements()
1655 private function _debug($text)
1657 if($this->fromcmd) {
1664 * check if specified MIME type is supported
1666 public function checkifImageSupported($mime)
1668 if(in_array($mime, Array("image/jpeg")))
1673 } // checkifImageSupported()
1675 public function _error($text)
1677 switch($this->cfg->logging) {
1679 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1686 error_log($text, 3, $his->cfg->log_file);
1693 * output calendard input fields
1695 private function get_calendar($mode)
1697 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1698 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1699 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1701 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1702 if(!isset($_SESSION[$mode .'_date']))
1703 $output.= " disabled=\"disabled\"";
1705 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1706 if(!isset($_SESSION[$mode .'_date']))
1707 $output.= " disabled=\"disabled\"";
1709 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1710 if(!isset($_SESSION[$mode .'_date']))
1711 $output.= " disabled=\"disabled\"";
1719 * output calendar matrix
1721 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1723 if (!isset($year)) $year = date('Y');
1724 if (!isset($month)) $month = date('m');
1725 if (!isset($day)) $day = date('d');
1730 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1731 require_once CALENDAR_ROOT.'Day.php';
1734 $month = new Calendar_Month_Weekdays($year,$month);
1737 $prevStamp = $month->prevMonth(true);
1738 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1739 $nextStamp = $month->nextMonth(true);
1740 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1742 $selectedDays = array (
1743 new Calendar_Day($year,$month,$day),
1744 new Calendar_Day($year,12,25),
1747 // Build the days in the month
1748 $month->build($selectedDays);
1750 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1751 $this->tmpl->assign('prev_month', $prev);
1752 $this->tmpl->assign('next_month', $next);
1754 while ( $day = $month->fetch() ) {
1756 if(!isset($matrix[$rows]))
1757 $matrix[$rows] = Array();
1761 $dayStamp = $day->thisDay(true);
1762 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1764 // isFirst() to find start of week
1765 if ( $day->isFirst() )
1768 if ( $day->isSelected() ) {
1769 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1770 } else if ( $day->isEmpty() ) {
1771 $string.= "<td> </td>\n";
1773 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1776 // isLast() to find end of week
1777 if ( $day->isLast() )
1778 $string.= "</tr>\n";
1780 $matrix[$rows][$cols] = $string;
1790 $this->tmpl->assign('matrix', $matrix);
1791 $this->tmpl->assign('rows', $rows);
1792 $this->tmpl->show("calendar.tpl");
1794 } // get_calendar_matrix()
1797 * output export page
1799 public function getExport($mode)
1801 $pictures = $this->getPhotoSelection();
1802 $current_tags = $this->getCurrentTags();
1804 foreach($pictures as $picture) {
1806 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1807 if($current_tags != "") {
1808 $orig_url.= "&tags=". $current_tags;
1810 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1811 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1814 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1819 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1820 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1824 // "[%pictureurl% %thumbnailurl%]"
1825 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1828 case 'MoinMoinList':
1829 // " * [%pictureurl% %thumbnailurl%]"
1830 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1841 public function getRSSFeed()
1843 Header("Content-type: text/xml; charset=utf-8");
1844 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1847 xmlns:media="http://search.yahoo.com/mrss/"
1848 xmlns:dc="http://purl.org/dc/elements/1.1/"
1851 <title>phpfspot</title>
1852 <description>phpfspot RSS feed</description>
1853 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1854 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1855 <generator>phpfspot</generator>
1858 $pictures = $this->getPhotoSelection();
1859 $current_tags = $this->getCurrentTags();
1861 foreach($pictures as $picture) {
1863 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1864 if($current_tags != "") {
1865 $orig_url.= "&tags=". $current_tags;
1867 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1868 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1871 $details = $this->get_photo_details($picture);
1873 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1874 $thumb_html = htmlspecialchars("
1875 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1877 ". $details['description']);
1879 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1880 $meta = $this->get_meta_informations($orig_path);
1881 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1885 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1886 <link><?php print htmlspecialchars($orig_url); ?></link>
1887 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1888 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1890 <?php print $thumb_html; ?>
1892 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1907 * return all selected tags as one string
1909 private function getCurrentTags()
1912 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1913 foreach($_SESSION['selected_tags'] as $tag)
1914 $current_tags.= $tag .",";
1915 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1917 return $current_tags;
1919 } // getCurrentTags()
1922 * return the current photo
1924 public function getCurrentPhoto()
1926 if(isset($_SESSION['current_photo'])) {
1927 print $_SESSION['current_photo'];
1929 } // getCurrentPhoto()
1932 * tells the client browser what to do
1934 * this function is getting called via AJAX by the
1935 * client browsers. it will tell them what they have
1936 * to do next. This is necessary for directly jumping
1937 * into photo index or single photo view when the are
1938 * requested with specific URLs
1940 public function whatToDo()
1942 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1943 return "show_photo";
1945 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1946 return "showpi_tags";
1948 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1952 return "nothing special";
1957 * return the current process-user
1959 private function getuid()
1961 if($uid = posix_getuid()) {
1962 if($user = posix_getpwuid($uid)) {
1963 return $user['name'];
1972 * returns a select-dropdown box to select photo index sort parameters
1974 public function smarty_sort_select_list($params, &$smarty)
1978 foreach($this->sort_orders as $key => $value) {
1979 $output.= "<option value=\"". $key ."\"";
1980 if($key == $_SESSION['sort_order']) {
1981 $output.= " selected=\"selected\"";
1983 $output.= ">". $value ."</option>";
1988 } // smarty_sort_select_list()
1991 * returns the currently selected sort order
1993 private function get_sort_order()
1995 switch($_SESSION['sort_order']) {
1997 return " ORDER BY p.time ASC";
2000 return " ORDER BY p.time DESC";
2003 if($this->dbver < 9) {
2004 return " ORDER BY p.name ASC";
2007 return " ORDER BY basename(p.uri) ASC";
2011 if($this->dbver < 9) {
2012 return " ORDER BY p.name DESC";
2015 return " ORDER BY basename(p.uri) DESC";
2020 } // get_sort_order()
2023 * return the next to be shown slide show image
2025 * this function returns the URL of the next image
2026 * in the slideshow sequence.
2028 public function getNextSlideShowImage()
2030 $all_photos = $this->getPhotoSelection();
2032 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
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 } // getNextSlideShowImage()
2042 * return the previous to be shown slide show image
2044 * this function returns the URL of the previous image
2045 * in the slideshow sequence.
2047 public function getPrevSlideShowImage()
2049 $all_photos = $this->getPhotoSelection();
2051 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2052 $_SESSION['slideshow_img'] = 0;
2054 $_SESSION['slideshow_img']--;
2056 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2058 } // getPrevSlideShowImage()
2060 public function resetSlideShow()
2062 if(isset($_SESSION['slideshow_img']))
2063 unset($_SESSION['slideshow_img']);
2064 } // resetSlideShow()
2069 * this function will get all photos from the fspot
2070 * database and randomly return ONE entry
2072 * saddly there is yet no sqlite3 function which returns
2073 * the bulk result in array, so we have to fill up our
2076 public function get_random_photo()
2080 $result = $this->db->db_query("
2085 while($row = $this->db->db_fetch_object($result)) {
2086 array_push($all, $row['id']);
2089 return $all[array_rand($all)];
2091 } // get_random_photo()
2094 * validates provided date
2096 * this function validates if the provided date
2097 * contains a valid date and will return true
2100 public function isValidDate($date_str)
2102 $timestamp = strtotime($date_str);
2104 if(is_numeric($timestamp))
2112 * timestamp to string conversion
2114 private function ts2str($timestamp)
2116 return strftime("%Y-%m-%d", $timestamp);
2119 private function extractTags($tags_str)
2121 $not_validated = split(',', $_GET['tags']);
2122 $validated = array();
2124 foreach($not_validated as $tag) {
2125 if(is_numeric($tag))
2126 array_push($validated, $tag);
2134 * returns the full path to a thumbnail
2136 public function get_thumb_path($width, $photo)
2138 $md5 = $this->getMD5($photo);
2139 $sub_path = substr($md5, 0, 2);
2140 return $this->cfg->thumb_path
2148 } // get_thumb_path()
2151 * returns server's virtual host name
2153 private function get_server_name()
2155 return $_SERVER['SERVER_NAME'];
2156 } // get_server_name()
2159 * returns type of webprotocol which is
2162 private function get_web_protocol()
2164 if(!isset($_SERVER['HTTPS']))
2168 } // get_web_protocol()
2171 * return url to this phpfspot installation
2173 private function get_phpfspot_url()
2175 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2176 } // get_phpfspot_url()
2179 * check file exists and is readable
2181 * returns true, if everything is ok, otherwise false
2182 * if $silent is not set, this function will output and
2185 private function check_readable($file, $silent = null)
2187 if(!file_exists($file)) {
2189 print "File \"". $file ."\" does not exist.\n";
2193 if(!is_readable($file)) {
2195 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2201 } // check_readable()
2204 * check if all needed indices are present
2206 * this function checks, if some needed indices are already
2207 * present, or if not, create them on the fly. they are
2208 * necessary to speed up some queries like that one look for
2209 * all tags, when show_tags is specified in the configuration.
2211 private function checkDbIndices()
2213 $result = $this->db->db_exec("
2214 CREATE INDEX IF NOT EXISTS
2221 } // checkDbIndices()
2224 * retrive F-Spot database version
2226 * this function will return the F-Spot database version number
2227 * It is stored within the sqlite3 database in the table meta
2229 public function getFspotDBVersion()
2231 if($result = $this->db->db_fetchSingleRow("
2232 SELECT data as version
2235 name LIKE 'F-Spot Database Version'
2237 return $result['version'];
2241 } // getFspotDBVersion()
2244 * parse the provided URI and will returned the
2247 public function parse_uri($uri, $mode)
2249 if(($components = parse_url($uri)) !== false) {
2253 return basename($components['path']);
2256 return dirname($components['path']);
2259 return $components['path'];