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.3";
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 thumbs_per_page is now 0, unset it
116 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_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)) {
476 $this->_error("Can't open file ". $thumb_path ."\n");
480 $info = getimagesize($thumb_path);
482 $this->tmpl->assign('description', $details['description']);
483 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
485 $this->tmpl->assign('width', $info[0]);
486 $this->tmpl->assign('height', $info[1]);
487 $this->tmpl->assign('ExifMadeOn', $meta_date);
488 $this->tmpl->assign('ExifMadeWith', $meta_make);
489 $this->tmpl->assign('ExifOrigResolution', $meta_res);
490 $this->tmpl->assign('ExifFileSize', $meta_size);
492 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
493 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
494 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
496 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
497 $this->tmpl->assign('current', $current);
500 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
501 $this->tmpl->assign('prev_img', $previous_img);
505 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
506 $this->tmpl->assign('next_img', $next_img);
508 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
509 $this->tmpl->assign('photo_number', $i);
510 $this->tmpl->assign('photo_count', count($all_photos));
512 $this->tmpl->show("single_photo.tpl");
517 * all available tags and tag cloud
519 * this function outputs all available tags (time ordered)
520 * and in addition output them as tag cloud (tags which have
521 * many photos will appears more then others)
523 public function getAvailableTags()
525 /* retrive tags from database */
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()
597 /* retrive tags from database */
602 foreach($this->avail_tags as $tag)
604 // return all selected tags
605 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
606 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
611 $output = substr($output, 0, strlen($output)-2);
615 return "no tags selected";
618 } // getSelectedTags()
621 * add tag to users session variable
623 * this function will add the specified to users current
624 * tag selection. if a date search has been made before
625 * it will be now cleared
627 public function addTag($tag)
629 if(!isset($_SESSION['selected_tags']))
630 $_SESSION['selected_tags'] = Array();
632 if(isset($_SESSION['searchfor']))
633 unset($_SESSION['searchfor']);
635 if(!in_array($tag, $_SESSION['selected_tags']))
636 array_push($_SESSION['selected_tags'], $tag);
641 * remove tag to users session variable
643 * this function removes the specified tag from
644 * users current tag selection
646 public function delTag($tag)
648 if(isset($_SESSION['searchfor']))
649 unset($_SESSION['searchfor']);
651 if(isset($_SESSION['selected_tags'])) {
652 $key = array_search($tag, $_SESSION['selected_tags']);
653 unset($_SESSION['selected_tags'][$key]);
654 sort($_SESSION['selected_tags']);
660 * reset tag selection
662 * if there is any tag selection, it will be
665 public function resetTags()
667 if(isset($_SESSION['selected_tags']))
668 unset($_SESSION['selected_tags']);
675 * if a specific photo was requested (external link)
676 * unset the session variable now
678 public function resetPhotoView()
680 if(isset($_SESSION['current_photo']))
681 unset($_SESSION['current_photo']);
683 } // resetPhotoView();
688 * if any tag search has taken place, reset
691 public function resetTagSearch()
693 if(isset($_SESSION['searchfor']))
694 unset($_SESSION['searchfor']);
696 } // resetTagSearch()
701 * if any date search has taken place, reset
704 public function resetDateSearch()
706 if(isset($_SESSION['from_date']))
707 unset($_SESSION['from_date']);
708 if(isset($_SESSION['to_date']))
709 unset($_SESSION['to_date']);
711 } // resetDateSearch();
714 * return all photo according selection
716 * this function returns all photos based on
717 * the tag-selection, tag- or date-search.
718 * the tag-search also has to take care of AND
719 * and OR conjunctions
721 public function getPhotoSelection()
723 $matched_photos = Array();
725 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
726 $from_date = $_SESSION['from_date'];
727 $to_date = $_SESSION['to_date'];
728 $additional_where_cond = "
729 p.time>='". $from_date ."'
731 p.time<='". $to_date ."'
735 if(isset($_SESSION['sort_order'])) {
736 $order_str = $this->get_sort_order();
739 /* return a search result */
740 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
742 SELECT DISTINCT pt1.photo_id
744 INNER JOIN photo_tags pt2
745 ON pt1.photo_id=pt2.photo_id
752 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
754 if(isset($additional_where_cond))
755 $query_str.= "AND ". $additional_where_cond ." ";
757 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
758 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
761 if(isset($order_str))
762 $query_str.= $order_str;
764 $result = $this->db->db_query($query_str);
765 while($row = $this->db->db_fetch_object($result)) {
766 array_push($matched_photos, $row['photo_id']);
768 return $matched_photos;
771 /* return according the selected tags */
772 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
774 foreach($_SESSION['selected_tags'] as $tag)
775 $selected.= $tag .",";
776 $selected = substr($selected, 0, strlen($selected)-1);
778 /* photo has to match at least on of the selected tags */
779 if($_SESSION['tag_condition'] == 'or') {
781 SELECT DISTINCT pt1.photo_id
783 INNER JOIN photo_tags pt2
784 ON pt1.photo_id=pt2.photo_id
789 WHERE pt1.tag_id IN (". $selected .")
791 if(isset($additional_where_cond))
792 $query_str.= "AND ". $additional_where_cond ." ";
794 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
795 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
798 if(isset($order_str))
799 $query_str.= $order_str;
801 /* photo has to match all selected tags */
802 elseif($_SESSION['tag_condition'] == 'and') {
804 if(count($_SESSION['selected_tags']) >= 32) {
805 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
806 print "evaluate your tag selection. Please remove some tags from your selection.\n";
810 /* Join together a table looking like
812 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
814 so the query can quickly return all images matching the
815 selected tags in an AND condition
820 SELECT DISTINCT pt1.photo_id
824 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
831 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
833 INNER JOIN photo_tags pt". ($i+2) ."
834 ON pt1.photo_id=pt". ($i+2) .".photo_id
841 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
842 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
844 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
847 if(isset($additional_where_cond))
848 $query_str.= "AND ". $additional_where_cond;
850 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
851 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
854 if(isset($order_str))
855 $query_str.= $order_str;
859 $result = $this->db->db_query($query_str);
860 while($row = $this->db->db_fetch_object($result)) {
861 array_push($matched_photos, $row['photo_id']);
863 return $matched_photos;
866 /* return all available photos */
870 LEFT JOIN photo_tags pt
876 if(isset($additional_where_cond))
877 $query_str.= "WHERE ". $additional_where_cond ." ";
879 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
880 if(isset($additional_where_cond))
881 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
883 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
886 if(isset($order_str))
887 $query_str.= $order_str;
889 $result = $this->db->db_query($query_str);
890 while($row = $this->db->db_fetch_object($result)) {
891 array_push($matched_photos, $row['id']);
893 return $matched_photos;
895 } // getPhotoSelection()
898 * control HTML ouput for photo index
900 * this function provides all the necessary information
901 * for the photo index template.
903 public function showPhotoIndex()
905 $photos = $this->getPhotoSelection();
907 $count = count($photos);
909 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
910 $anchor = $_SESSION['begin_with'];
912 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
918 elseif($this->cfg->thumbs_per_page > 0) {
920 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
924 $begin_with = $_SESSION['begin_with'];
927 $end_with = $begin_with + $this->cfg->thumbs_per_page;
931 $images[$thumbs] = Array();
932 $img_height[$thumbs] = Array();
933 $img_width[$thumbs] = Array();
934 $img_id[$thumbs] = Array();
935 $img_name[$thumbs] = Array();
936 $img_title = Array();
938 for($i = $begin_with; $i < $end_with; $i++) {
940 if(isset($photos[$i])) {
942 $images[$thumbs] = $photos[$i];
943 $img_id[$thumbs] = $i;
944 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
945 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
947 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
949 if(file_exists($thumb_path)) {
950 $info = getimagesize($thumb_path);
951 $img_width[$thumbs] = $info[0];
952 $img_height[$thumbs] = $info[1];
958 // +1 for for smarty's selection iteration
961 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
962 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
964 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
965 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
966 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
969 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
970 $this->tmpl->assign('tag_result', 1);
973 /* do we have to display the page selector ? */
974 if($this->cfg->thumbs_per_page != 0) {
978 /* calculate the page switchers */
979 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
980 $next_start = $begin_with + $this->cfg->thumbs_per_page;
983 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
984 if($end_with < $count)
985 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
987 $photo_per_page = $this->cfg->thumbs_per_page;
988 $last_page = ceil($count / $photo_per_page);
990 /* get the current selected page */
991 if($begin_with == 0) {
995 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1002 for($i = 1; $i <= $last_page; $i++) {
1004 if($current_page == $i)
1005 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1006 elseif($current_page-1 == $i || $current_page+1 == $i)
1007 $style = "style=\"font-size: 105%;\"";
1008 elseif(($current_page-5 >= $i) && ($i != 1) ||
1009 ($current_page+5 <= $i) && ($i != $last_page))
1010 $style = "style=\"font-size: 75%;\"";
1014 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1017 $select.= ">". $i ."</a> ";
1019 // until 9 pages we show the selector from 1-9
1020 if($last_page <= 9) {
1021 $page_select.= $select;
1024 if($i == 1 /* first page */ ||
1025 $i == $last_page /* last page */ ||
1026 $i == $current_page /* current page */ ||
1027 $i == ceil($last_page * 0.25) /* first quater */ ||
1028 $i == ceil($last_page * 0.5) /* half */ ||
1029 $i == ceil($last_page * 0.75) /* third quater */ ||
1030 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1031 (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 */ ||
1032 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1033 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1035 $page_select.= $select;
1043 $page_select.= "......... ";
1048 /* only show the page selector if we have more then one page */
1050 $this->tmpl->assign('page_selector', $page_select);
1054 $current_tags = $this->getCurrentTags();
1055 $extern_link = "index.php?mode=showpi";
1056 $rss_link = "index.php?mode=rss";
1057 if($current_tags != "") {
1058 $extern_link.= "&tags=". $current_tags;
1059 $rss_link.= "&tags=". $current_tags;
1061 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1062 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1063 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1066 $export_link = "index.php?mode=export";
1067 $slideshow_link = "index.php?mode=slideshow";
1069 $this->tmpl->assign('extern_link', $extern_link);
1070 $this->tmpl->assign('slideshow_link', $slideshow_link);
1071 $this->tmpl->assign('export_link', $export_link);
1072 $this->tmpl->assign('rss_link', $rss_link);
1073 $this->tmpl->assign('count', $count);
1074 $this->tmpl->assign('width', $this->cfg->thumb_width);
1075 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1076 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1077 $this->tmpl->assign('images', $images);
1078 $this->tmpl->assign('img_width', $img_width);
1079 $this->tmpl->assign('img_height', $img_height);
1080 $this->tmpl->assign('img_id', $img_id);
1081 $this->tmpl->assign('img_name', $img_name);
1082 $this->tmpl->assign('img_title', $img_title);
1083 $this->tmpl->assign('thumbs', $thumbs);
1085 $this->tmpl->show("photo_index.tpl");
1088 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1090 } // showPhotoIndex()
1093 * show credit template
1095 public function showCredits()
1097 $this->tmpl->assign('version', $this->cfg->version);
1098 $this->tmpl->assign('product', $this->cfg->product);
1099 $this->tmpl->assign('db_version', $this->dbver);
1100 $this->tmpl->show("credits.tpl");
1105 * create_thumbnails for the requested width
1107 * this function creates image thumbnails of $orig_image
1108 * stored as $thumb_image. It will check if the image is
1109 * in a supported format, if necessary rotate the image
1110 * (based on EXIF orientation meta headers) and re-sizing.
1112 public function create_thumbnail($orig_image, $thumb_image, $width)
1114 if(!file_exists($orig_image)) {
1118 $details = getimagesize($orig_image);
1120 /* check if original photo is a support image type */
1121 if(!$this->checkifImageSupported($details['mime']))
1124 $meta = $this->get_meta_informations($orig_image);
1130 switch($meta['Orientation']) {
1131 case 1: /* top, left */
1132 /* nothing to do */ break;
1133 case 2: /* top, right */
1134 $rotate = 0; $flip_hori = true; break;
1135 case 3: /* bottom, left */
1136 $rotate = 180; break;
1137 case 4: /* bottom, right */
1138 $flip_vert = true; break;
1139 case 5: /* left side, top */
1140 $rotate = 90; $flip_vert = true; break;
1141 case 6: /* right side, top */
1142 $rotate = 90; break;
1143 case 7: /* left side, bottom */
1144 $rotate = 270; $flip_vert = true; break;
1145 case 8: /* right side, bottom */
1146 $rotate = 270; break;
1149 $src_img = @imagecreatefromjpeg($orig_image);
1152 print "Can't load image from ". $orig_image ."\n";
1156 /* grabs the height and width */
1157 $cur_width = imagesx($src_img);
1158 $cur_height = imagesy($src_img);
1160 // If requested width is more then the actual image width,
1161 // do not generate a thumbnail, instead safe the original
1162 // as thumbnail but with lower quality. But if the image
1163 // is to heigh too, then we still have to resize it.
1164 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1165 $result = imagejpeg($src_img, $thumb_image, 75);
1166 imagedestroy($src_img);
1170 // If the image will be rotate because EXIF orientation said so
1171 // 'virtually rotate' the image for further calculations
1172 if($rotate == 90 || $rotate == 270) {
1174 $cur_width = $cur_height;
1178 /* calculates aspect ratio */
1179 $aspect_ratio = $cur_height / $cur_width;
1182 if($aspect_ratio < 1) {
1184 $new_h = abs($new_w * $aspect_ratio);
1186 /* 'virtually' rotate the image and calculate it's ratio */
1187 $tmp_w = $cur_height;
1188 $tmp_h = $cur_width;
1189 /* now get the ratio from the 'rotated' image */
1190 $tmp_ratio = $tmp_h/$tmp_w;
1191 /* now calculate the new dimensions */
1193 $tmp_h = abs($tmp_w * $tmp_ratio);
1195 // now that we know, how high they photo should be, if it
1196 // gets rotated, use this high to scale the image
1198 $new_w = abs($new_h / $aspect_ratio);
1200 // If the image will be rotate because EXIF orientation said so
1201 // now 'virtually rotate' back the image for the image manipulation
1202 if($rotate == 90 || $rotate == 270) {
1209 /* creates new image of that size */
1210 $dst_img = imagecreatetruecolor($new_w, $new_h);
1212 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1214 /* copies resized portion of original image into new image */
1215 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1217 /* needs the image to be flipped horizontal? */
1219 $this->_debug("(FLIP)");
1220 $dst_img = $this->flipImage($dst_img, 'hori');
1222 /* needs the image to be flipped vertical? */
1224 $this->_debug("(FLIP)");
1225 $dst_img = $this->flipImage($dst_img, 'vert');
1229 $this->_debug("(ROTATE)");
1230 $dst_img = $this->rotateImage($dst_img, $rotate);
1233 /* write down new generated file */
1234 $result = imagejpeg($dst_img, $thumb_image, 75);
1236 /* free your mind */
1237 imagedestroy($dst_img);
1238 imagedestroy($src_img);
1240 if($result === false) {
1241 print "Can't write thumbnail ". $thumb_image ."\n";
1247 } // create_thumbnail()
1250 * return all exif meta data from the file
1252 public function get_meta_informations($file)
1254 return exif_read_data($file);
1256 } // get_meta_informations()
1259 * create phpfspot own sqlite database
1261 * this function creates phpfspots own sqlite database
1262 * if it does not exist yet. this own is used to store
1263 * some necessary informations (md5 sum's, ...).
1265 public function check_config_table()
1267 // if the config table doesn't exist yet, create it
1268 if(!$this->cfg_db->db_check_table_exists("images")) {
1269 $this->cfg_db->db_exec("
1270 CREATE TABLE images (
1271 img_idx int primary key,
1277 } // check_config_table
1280 * Generates a thumbnail from photo idx
1282 * This function will generate JPEG thumbnails from provided F-Spot photo
1285 * 1. Check if all thumbnail generations (width) are already in place and
1287 * 2. Check if the md5sum of the original file has changed
1288 * 3. Generate the thumbnails if needed
1290 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1294 $resolutions = Array(
1295 $this->cfg->thumb_width,
1296 $this->cfg->photo_width,
1297 $this->cfg->mini_width,
1300 /* get details from F-Spot's database */
1301 $details = $this->get_photo_details($idx);
1303 /* calculate file MD5 sum */
1304 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1306 if(!file_exists($full_path)) {
1307 $this->_error("File ". $full_path ." does not exist\n");
1311 if(!is_readable($full_path)) {
1312 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1316 $file_md5 = md5_file($full_path);
1318 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1322 foreach($resolutions as $resolution) {
1324 $generate_it = false;
1326 $thumb_sub_path = substr($file_md5, 0, 2);
1327 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1329 if(!file_exists(dirname($thumb_path))) {
1330 mkdir(dirname($thumb_path), 0755);
1333 /* if the thumbnail file doesn't exist, create it */
1334 if(!file_exists($thumb_path)) {
1335 $generate_it = true;
1337 /* if the file hasn't changed there is no need to regen the thumb */
1338 elseif($file_md5 != $this->getMD5($idx) || $force) {
1339 $generate_it = true;
1342 if($generate_it || $overwrite) {
1344 $this->_debug(" ". $resolution ."px");
1345 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1353 $this->_debug(" already exist");
1356 /* set the new/changed MD5 sum for the current photo */
1358 $this->setMD5($idx, $file_md5);
1361 $this->_debug("\n");
1366 * returns stored md5 sum for a specific photo
1368 * this function queries the phpfspot database for a
1369 * stored MD5 checksum of the specified photo
1371 public function getMD5($idx)
1373 $result = $this->cfg_db->db_query("
1376 WHERE img_idx='". $idx ."'
1382 $img = $this->cfg_db->db_fetch_object($result);
1383 return $img['img_md5'];
1388 * set MD5 sum for the specific photo
1390 private function setMD5($idx, $md5)
1392 $result = $this->cfg_db->db_exec("
1393 REPLACE INTO images (img_idx, img_md5)
1394 VALUES ('". $idx ."', '". $md5 ."')
1400 * store current tag condition
1402 * this function stores the current tag condition
1403 * (AND or OR) in the users session variables
1405 public function setTagCondition($mode)
1407 $_SESSION['tag_condition'] = $mode;
1409 } // setTagCondition()
1412 * invoke tag & date search
1414 * this function will return all matching tags and store
1415 * them in the session variable selected_tags. furthermore
1416 * it also handles the date search.
1417 * getPhotoSelection() will then only return the matching
1420 public function startSearch($searchfor, $from = 0, $to = 0)
1424 $_SESSION['searchfor'] = $searchfor;
1427 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1429 unset($_SESSION['from_date']);
1432 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1434 unset($_SESSION['to_date']);
1436 if($searchfor != "") {
1437 /* new search, reset the current selected tags */
1438 $_SESSION['selected_tags'] = Array();
1439 foreach($this->avail_tags as $tag) {
1440 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1441 array_push($_SESSION['selected_tags'], $tag);
1448 * updates sort order in session variable
1450 * this function is invoked by RPC and will sort the requested
1451 * sort order in the session variable.
1453 public function updateSortOrder($order)
1455 if(isset($this->sort_orders[$order])) {
1456 $_SESSION['sort_order'] = $order;
1460 return "unkown error";
1462 } // updateSortOrder()
1467 * this function rotates the image according the
1470 private function rotateImage($img, $degrees)
1472 if(function_exists("imagerotate")) {
1473 $img = imagerotate($img, $degrees, 0);
1475 function imagerotate($src_img, $angle)
1477 $src_x = imagesx($src_img);
1478 $src_y = imagesy($src_img);
1479 if ($angle == 180) {
1483 elseif ($src_x <= $src_y) {
1487 elseif ($src_x >= $src_y) {
1492 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1493 imagealphablending($rotate, false);
1498 for ($y = 0; $y < ($src_y); $y++) {
1499 for ($x = 0; $x < ($src_x); $x++) {
1500 $color = imagecolorat($src_img, $x, $y);
1501 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1507 for ($y = 0; $y < ($src_y); $y++) {
1508 for ($x = 0; $x < ($src_x); $x++) {
1509 $color = imagecolorat($src_img, $x, $y);
1510 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1516 for ($y = 0; $y < ($src_y); $y++) {
1517 for ($x = 0; $x < ($src_x); $x++) {
1518 $color = imagecolorat($src_img, $x, $y);
1519 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1533 $img = imagerotate($img, $degrees);
1542 * returns flipped image
1544 * this function will return an either horizontal or
1545 * vertical flipped truecolor image.
1547 private function flipImage($image, $mode)
1549 $w = imagesx($image);
1550 $h = imagesy($image);
1551 $flipped = imagecreatetruecolor($w, $h);
1555 for ($y = 0; $y < $h; $y++) {
1556 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1560 for ($x = 0; $x < $w; $x++) {
1561 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1571 * return all assigned tags for the specified photo
1573 private function get_photo_tags($idx)
1575 $result = $this->db->db_query("
1578 INNER JOIN photo_tags pt
1580 WHERE pt.photo_id='". $idx ."'
1585 while($row = $this->db->db_fetch_object($result))
1586 $tags[$row['id']] = $row['name'];
1590 } // get_photo_tags()
1593 * create on-the-fly images with text within
1595 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1597 if (strlen($color) != 6)
1600 $int = hexdec($color);
1601 $h = imagefontheight($font);
1602 $fw = imagefontwidth($font);
1603 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1604 $lines = count($txt);
1605 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1606 $bg = imagecolorallocate($im, 255, 255, 255);
1607 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1610 foreach ($txt as $text) {
1611 $x = (($w - ($fw * strlen($text))) / 2);
1612 imagestring($im, $font, $x, $y, $text, $color);
1613 $y += ($h + $space);
1616 Header("Content-type: image/png");
1619 } // showTextImage()
1622 * check if all requirements are met
1624 private function checkRequirements()
1626 if(!function_exists("imagecreatefromjpeg")) {
1627 print "PHP GD library extension is missing<br />\n";
1631 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1632 print "PHP SQLite3 library extension is missing<br />\n";
1636 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1637 ini_set('track_errors', 1);
1638 @include_once 'HTML/AJAX/Server.php';
1639 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1640 print "PEAR HTML_AJAX package is missing<br />\n";
1643 @include_once 'Calendar/Calendar.php';
1644 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1645 print "PEAR Calendar package is missing<br />\n";
1648 @include_once 'Console/Getopt.php';
1649 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1650 print "PEAR Console_Getopt package is missing<br />\n";
1653 ini_restore('track_errors');
1660 } // checkRequirements()
1662 private function _debug($text)
1664 if($this->fromcmd) {
1671 * check if specified MIME type is supported
1673 public function checkifImageSupported($mime)
1675 if(in_array($mime, Array("image/jpeg")))
1680 } // checkifImageSupported()
1682 public function _error($text)
1684 switch($this->cfg->logging) {
1686 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1693 error_log($text, 3, $his->cfg->log_file);
1700 * output calendard input fields
1702 private function get_calendar($mode)
1704 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1705 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1706 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1708 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1709 if(!isset($_SESSION[$mode .'_date']))
1710 $output.= " disabled=\"disabled\"";
1712 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1713 if(!isset($_SESSION[$mode .'_date']))
1714 $output.= " disabled=\"disabled\"";
1716 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1717 if(!isset($_SESSION[$mode .'_date']))
1718 $output.= " disabled=\"disabled\"";
1726 * output calendar matrix
1728 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1730 if (!isset($year)) $year = date('Y');
1731 if (!isset($month)) $month = date('m');
1732 if (!isset($day)) $day = date('d');
1737 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1738 require_once CALENDAR_ROOT.'Day.php';
1741 $month = new Calendar_Month_Weekdays($year,$month);
1744 $prevStamp = $month->prevMonth(true);
1745 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1746 $nextStamp = $month->nextMonth(true);
1747 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1749 $selectedDays = array (
1750 new Calendar_Day($year,$month,$day),
1751 new Calendar_Day($year,12,25),
1754 // Build the days in the month
1755 $month->build($selectedDays);
1757 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1758 $this->tmpl->assign('prev_month', $prev);
1759 $this->tmpl->assign('next_month', $next);
1761 while ( $day = $month->fetch() ) {
1763 if(!isset($matrix[$rows]))
1764 $matrix[$rows] = Array();
1768 $dayStamp = $day->thisDay(true);
1769 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1771 // isFirst() to find start of week
1772 if ( $day->isFirst() )
1775 if ( $day->isSelected() ) {
1776 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1777 } else if ( $day->isEmpty() ) {
1778 $string.= "<td> </td>\n";
1780 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1783 // isLast() to find end of week
1784 if ( $day->isLast() )
1785 $string.= "</tr>\n";
1787 $matrix[$rows][$cols] = $string;
1797 $this->tmpl->assign('matrix', $matrix);
1798 $this->tmpl->assign('rows', $rows);
1799 $this->tmpl->show("calendar.tpl");
1801 } // get_calendar_matrix()
1804 * output export page
1806 public function getExport($mode)
1808 $pictures = $this->getPhotoSelection();
1809 $current_tags = $this->getCurrentTags();
1811 foreach($pictures as $picture) {
1813 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1814 if($current_tags != "") {
1815 $orig_url.= "&tags=". $current_tags;
1817 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1818 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1821 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1826 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1827 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1831 // "[%pictureurl% %thumbnailurl%]"
1832 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1835 case 'MoinMoinList':
1836 // " * [%pictureurl% %thumbnailurl%]"
1837 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1848 public function getRSSFeed()
1850 Header("Content-type: text/xml; charset=utf-8");
1851 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1854 xmlns:media="http://search.yahoo.com/mrss/"
1855 xmlns:dc="http://purl.org/dc/elements/1.1/"
1858 <title>phpfspot</title>
1859 <description>phpfspot RSS feed</description>
1860 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1861 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1862 <generator>phpfspot</generator>
1865 $pictures = $this->getPhotoSelection();
1866 $current_tags = $this->getCurrentTags();
1868 foreach($pictures as $picture) {
1870 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1871 if($current_tags != "") {
1872 $orig_url.= "&tags=". $current_tags;
1874 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1875 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1878 $details = $this->get_photo_details($picture);
1880 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1881 $thumb_html = htmlspecialchars("
1882 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1884 ". $details['description']);
1886 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1887 $meta = $this->get_meta_informations($orig_path);
1888 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1892 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1893 <link><?php print htmlspecialchars($orig_url); ?></link>
1894 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1895 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1897 <?php print $thumb_html; ?>
1899 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1914 * return all selected tags as one string
1916 private function getCurrentTags()
1919 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1920 foreach($_SESSION['selected_tags'] as $tag)
1921 $current_tags.= $tag .",";
1922 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1924 return $current_tags;
1926 } // getCurrentTags()
1929 * return the current photo
1931 public function getCurrentPhoto()
1933 if(isset($_SESSION['current_photo'])) {
1934 print $_SESSION['current_photo'];
1936 } // getCurrentPhoto()
1939 * tells the client browser what to do
1941 * this function is getting called via AJAX by the
1942 * client browsers. it will tell them what they have
1943 * to do next. This is necessary for directly jumping
1944 * into photo index or single photo view when the are
1945 * requested with specific URLs
1947 public function whatToDo()
1949 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1950 return "show_photo";
1952 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1953 return "showpi_tags";
1955 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1959 return "nothing special";
1964 * return the current process-user
1966 private function getuid()
1968 if($uid = posix_getuid()) {
1969 if($user = posix_getpwuid($uid)) {
1970 return $user['name'];
1979 * returns a select-dropdown box to select photo index sort parameters
1981 public function smarty_sort_select_list($params, &$smarty)
1985 foreach($this->sort_orders as $key => $value) {
1986 $output.= "<option value=\"". $key ."\"";
1987 if($key == $_SESSION['sort_order']) {
1988 $output.= " selected=\"selected\"";
1990 $output.= ">". $value ."</option>";
1995 } // smarty_sort_select_list()
1998 * returns the currently selected sort order
2000 private function get_sort_order()
2002 switch($_SESSION['sort_order']) {
2004 return " ORDER BY p.time ASC";
2007 return " ORDER BY p.time DESC";
2010 if($this->dbver < 9) {
2011 return " ORDER BY p.name ASC";
2014 return " ORDER BY basename(p.uri) ASC";
2018 if($this->dbver < 9) {
2019 return " ORDER BY p.name DESC";
2022 return " ORDER BY basename(p.uri) DESC";
2027 } // get_sort_order()
2030 * return the next to be shown slide show image
2032 * this function returns the URL of the next image
2033 * in the slideshow sequence.
2035 public function getNextSlideShowImage()
2037 $all_photos = $this->getPhotoSelection();
2039 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2040 $_SESSION['slideshow_img'] = 0;
2042 $_SESSION['slideshow_img']++;
2044 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2046 } // getNextSlideShowImage()
2049 * return the previous to be shown slide show image
2051 * this function returns the URL of the previous image
2052 * in the slideshow sequence.
2054 public function getPrevSlideShowImage()
2056 $all_photos = $this->getPhotoSelection();
2058 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2059 $_SESSION['slideshow_img'] = 0;
2061 $_SESSION['slideshow_img']--;
2063 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2065 } // getPrevSlideShowImage()
2067 public function resetSlideShow()
2069 if(isset($_SESSION['slideshow_img']))
2070 unset($_SESSION['slideshow_img']);
2071 } // resetSlideShow()
2076 * this function will get all photos from the fspot
2077 * database and randomly return ONE entry
2079 * saddly there is yet no sqlite3 function which returns
2080 * the bulk result in array, so we have to fill up our
2083 public function get_random_photo()
2087 $result = $this->db->db_query("
2092 while($row = $this->db->db_fetch_object($result)) {
2093 array_push($all, $row['id']);
2096 return $all[array_rand($all)];
2098 } // get_random_photo()
2101 * validates provided date
2103 * this function validates if the provided date
2104 * contains a valid date and will return true
2107 public function isValidDate($date_str)
2109 $timestamp = strtotime($date_str);
2111 if(is_numeric($timestamp))
2119 * timestamp to string conversion
2121 private function ts2str($timestamp)
2123 return strftime("%Y-%m-%d", $timestamp);
2126 private function extractTags($tags_str)
2128 $not_validated = split(',', $_GET['tags']);
2129 $validated = array();
2131 foreach($not_validated as $tag) {
2132 if(is_numeric($tag))
2133 array_push($validated, $tag);
2141 * returns the full path to a thumbnail
2143 public function get_thumb_path($width, $photo)
2145 $md5 = $this->getMD5($photo);
2146 $sub_path = substr($md5, 0, 2);
2147 return $this->cfg->thumb_path
2155 } // get_thumb_path()
2158 * returns server's virtual host name
2160 private function get_server_name()
2162 return $_SERVER['SERVER_NAME'];
2163 } // get_server_name()
2166 * returns type of webprotocol which is
2169 private function get_web_protocol()
2171 if(!isset($_SERVER['HTTPS']))
2175 } // get_web_protocol()
2178 * return url to this phpfspot installation
2180 private function get_phpfspot_url()
2182 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2183 } // get_phpfspot_url()
2186 * check file exists and is readable
2188 * returns true, if everything is ok, otherwise false
2189 * if $silent is not set, this function will output and
2192 private function check_readable($file, $silent = null)
2194 if(!file_exists($file)) {
2196 print "File \"". $file ."\" does not exist.\n";
2200 if(!is_readable($file)) {
2202 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2208 } // check_readable()
2211 * check if all needed indices are present
2213 * this function checks, if some needed indices are already
2214 * present, or if not, create them on the fly. they are
2215 * necessary to speed up some queries like that one look for
2216 * all tags, when show_tags is specified in the configuration.
2218 private function checkDbIndices()
2220 $result = $this->db->db_exec("
2221 CREATE INDEX IF NOT EXISTS
2228 } // checkDbIndices()
2231 * retrive F-Spot database version
2233 * this function will return the F-Spot database version number
2234 * It is stored within the sqlite3 database in the table meta
2236 public function getFspotDBVersion()
2238 if($result = $this->db->db_fetchSingleRow("
2239 SELECT data as version
2242 name LIKE 'F-Spot Database Version'
2244 return $result['version'];
2248 } // getFspotDBVersion()
2251 * parse the provided URI and will returned the
2254 public function parse_uri($uri, $mode)
2256 if(($components = parse_url($uri)) !== false) {
2260 return basename($components['path']);
2263 return dirname($components['path']);
2266 return $components['path'];