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 ↓',
57 'tags_asc' => 'Tags ↑',
58 'tags_desc' => 'Tags ↓',
61 /* Check necessary requirements */
62 if(!$this->checkRequirements()) {
66 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
67 if(!is_writeable($this->cfg->fspot_db)) {
68 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
72 $this->dbver = $this->getFspotDBVersion();
74 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
75 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
79 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
80 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
84 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
85 if(!is_writeable($this->cfg->phpfspot_db)) {
86 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
90 $this->check_config_table();
92 /* include Smarty template engine */
93 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
96 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
97 /* overload Smarty class if our own template handler */
98 require_once "phpfspot_tmpl.php";
99 $this->tmpl = new PHPFSPOT_TMPL($this);
101 /* check if all necessary indices exist */
102 $this->checkDbIndices();
104 /* if session is not yet started, do it now */
105 if(session_id() == "")
108 if(!isset($_SESSION['tag_condition']))
109 $_SESSION['tag_condition'] = 'or';
111 if(!isset($_SESSION['sort_order']))
112 $_SESSION['sort_order'] = 'date_asc';
114 if(!isset($_SESSION['searchfor']))
115 $_SESSION['searchfor'] = '';
117 // if begin_with is still set but thumbs_per_page is now 0, unset it
118 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
119 unset($_SESSION['begin_with']);
123 public function __destruct()
129 * show - generate html output
131 * this function can be called after the constructor has
132 * prepared everyhing. it will load the index.tpl smarty
133 * template. if necessary it will registere pre-selects
134 * (photo index, photo, tag search, date search) into
137 public function show()
139 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
140 $this->tmpl->assign('page_title', $this->cfg->page_title);
141 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
142 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
144 if(isset($_GET['mode'])) {
146 $_SESSION['start_action'] = $_GET['mode'];
148 switch($_GET['mode']) {
150 if(isset($_GET['tags'])) {
151 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
153 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
154 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
156 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
157 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
161 if(isset($_GET['tags'])) {
162 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
163 $_SESSION['start_action'] = 'showp';
165 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
166 $_SESSION['current_photo'] = $_GET['id'];
167 $_SESSION['start_action'] = 'showp';
169 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
170 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
172 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
173 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
177 $this->tmpl->show("export.tpl");
181 $this->tmpl->show("slideshow.tpl");
185 if(isset($_GET['tags'])) {
186 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
188 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
189 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
191 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
192 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
200 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
201 $this->tmpl->assign('date_search_enabled', true);
203 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
204 $this->tmpl->assign('from_date', $this->get_calendar('from'));
205 $this->tmpl->assign('to_date', $this->get_calendar('to'));
206 $this->tmpl->assign('content_page', 'welcome.tpl');
207 $this->tmpl->show("index.tpl");
212 * get_tags - grab all tags of f-spot's database
214 * this function will get all available tags from
215 * the f-spot database and store them within two
216 * arrays within this class for later usage. in
217 * fact, if the user requests (hide_tags) it will
218 * opt-out some of them.
220 * this function is getting called once by show()
222 private function get_tags()
224 $this->avail_tags = Array();
227 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
230 DISTINCT t1.id as id, t1.name as name
233 INNER JOIN photo_tags
234 pt2 ON pt1.photo_id=pt2.photo_id
240 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
242 t1.sort_priority ASC";
244 $result = $this->db->db_query($query_str);
248 $result = $this->db->db_query("
251 ORDER BY sort_priority ASC
255 while($row = $this->db->db_fetch_object($result)) {
257 $tag_id = $row['id'];
258 $tag_name = $row['name'];
260 /* if the user has specified to ignore this tag in phpfspot's
261 configuration, ignore it here so it does not get added to
264 if(in_array($row['name'], $this->cfg->hide_tags))
267 /* if you include the following if-clause and the user has specified
268 to only show certain tags which are specified in phpfspot's
269 configuration, ignore all others so they will not be added to the
271 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
272 !in_array($row['name'], $this->cfg->show_tags))
276 $this->tags[$tag_id] = $tag_name;
277 $this->avail_tags[$count] = $tag_id;
285 * extract all photo details
287 * retrieve all available details from f-spot's
288 * database and return them as object
290 public function get_photo_details($idx)
292 if($this->dbver < 9) {
294 SELECT p.id, p.name, p.time, p.directory_path, p.description
300 SELECT p.id, p.uri, p.time, p.description
305 /* if show_tags is set, only return details for photos which
306 are specified to be shown
308 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
310 INNER JOIN photo_tags pt
314 WHERE p.id='". $idx ."'
315 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
319 WHERE p.id='". $idx ."'
323 if($result = $this->db->db_query($query_str)) {
325 $row = $this->db->db_fetch_object($result);
327 if($this->dbver < 9) {
328 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
337 } // get_photo_details
340 * returns aligned photo names
342 * this function returns aligned (length) names for
343 * an specific photo. If the length of the name exceeds
344 * $limit the name will be shrinked (...)
346 public function getPhotoName($idx, $limit = 0)
348 if($details = $this->get_photo_details($idx)) {
349 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
350 $name = $this->shrink_text($long_name, $limit);
360 * shrink text according provided limit
362 * If the length of the name exceeds $limit the
363 * text will be shortend and some content in between
364 * will be replaced with "..."
366 private function shrink_text($text, $limit)
368 if($limit != 0 && strlen($text) > $limit) {
369 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
377 * translate f-spoth photo path
379 * as the full-qualified path recorded in the f-spot database
380 * is usally not the same as on the webserver, this function
381 * will replace the path with that one specified in the cfg
383 public function translate_path($path, $width = 0)
385 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
390 * control HTML ouput for a single photo
392 * this function provides all the necessary information
393 * for the single photo template.
395 public function showPhoto($photo)
397 /* get all photos from the current photo selection */
398 $all_photos = $this->getPhotoSelection();
399 $count = count($all_photos);
401 for($i = 0; $i < $count; $i++) {
403 // $get_next will be set, when the photo which has to
404 // be displayed has been found - this means that the
405 // next available is in fact the NEXT image (for the
407 if(isset($get_next)) {
408 $next_img = $all_photos[$i];
412 /* the next photo is our NEXT photo */
413 if($all_photos[$i] == $photo) {
417 $previous_img = $all_photos[$i];
420 if($photo == $all_photos[$i]) {
425 $details = $this->get_photo_details($photo);
432 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
433 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
435 if(!file_exists($orig_path)) {
436 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
440 if(!is_readable($orig_path)) {
441 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
445 /* If the thumbnail doesn't exist yet, try to create it */
446 if(!file_exists($thumb_path)) {
447 $this->gen_thumb($photo, true);
448 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
451 /* get f-spot database meta information */
452 $meta = $this->get_meta_informations($orig_path);
454 /* If EXIF data are available, use them */
455 if(isset($meta['ExifImageWidth'])) {
456 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
458 $info = getimagesize($orig_path);
459 $meta_res = $info[0] ."x". $info[1];
462 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
463 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
464 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
466 $extern_link = "index.php?mode=showp&id=". $photo;
467 $current_tags = $this->getCurrentTags();
468 if($current_tags != "") {
469 $extern_link.= "&tags=". $current_tags;
471 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
472 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
475 $this->tmpl->assign('extern_link', $extern_link);
477 if(!file_exists($thumb_path)) {
478 $this->_error("Can't open file ". $thumb_path ."\n");
482 $info = getimagesize($thumb_path);
484 $this->tmpl->assign('description', $details['description']);
485 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
487 $this->tmpl->assign('width', $info[0]);
488 $this->tmpl->assign('height', $info[1]);
489 $this->tmpl->assign('ExifMadeOn', $meta_date);
490 $this->tmpl->assign('ExifMadeWith', $meta_make);
491 $this->tmpl->assign('ExifOrigResolution', $meta_res);
492 $this->tmpl->assign('ExifFileSize', $meta_size);
494 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
495 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
496 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
498 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
499 $this->tmpl->assign('current', $current);
502 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
503 $this->tmpl->assign('prev_img', $previous_img);
507 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
508 $this->tmpl->assign('next_img', $next_img);
510 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
511 $this->tmpl->assign('photo_number', $i);
512 $this->tmpl->assign('photo_count', count($all_photos));
514 $this->tmpl->show("single_photo.tpl");
519 * all available tags and tag cloud
521 * this function outputs all available tags (time ordered)
522 * and in addition output them as tag cloud (tags which have
523 * many photos will appears more then others)
525 public function getAvailableTags()
527 /* retrive tags from database */
532 $result = $this->db->db_query("
533 SELECT tag_id as id, count(tag_id) as quantity
543 while($row = $this->db->db_fetch_object($result)) {
544 $tags[$row['id']] = $row['quantity'];
547 // change these font sizes if you will
548 $max_size = 125; // max font size in %
549 $min_size = 75; // min font size in %
551 // get the largest and smallest array values
552 $max_qty = max(array_values($tags));
553 $min_qty = min(array_values($tags));
555 // find the range of values
556 $spread = $max_qty - $min_qty;
557 if (0 == $spread) { // we don't want to divide by zero
561 // determine the font-size increment
562 // this is the increase per tag quantity (times used)
563 $step = ($max_size - $min_size)/($spread);
565 // loop through our tag array
566 foreach ($tags as $key => $value) {
568 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
571 // calculate CSS font-size
572 // find the $value in excess of $min_qty
573 // multiply by the font-size increment ($size)
574 // and add the $min_size set above
575 $size = $min_size + (($value - $min_qty) * $step);
576 // uncomment if you want sizes in whole %:
579 if(isset($this->tags[$key])) {
580 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
585 $output = substr($output, 0, strlen($output)-2);
588 } // getAvailableTags()
591 * output all selected tags
593 * this function output all tags which have been selected
594 * by the user. the selected tags are stored in the
595 * session-variable $_SESSION['selected_tags']
597 public function getSelectedTags()
599 /* retrive tags from database */
604 foreach($this->avail_tags as $tag)
606 // return all selected tags
607 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
608 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
613 $output = substr($output, 0, strlen($output)-2);
617 return "no tags selected";
620 } // getSelectedTags()
623 * add tag to users session variable
625 * this function will add the specified to users current
626 * tag selection. if a date search has been made before
627 * it will be now cleared
629 public function addTag($tag)
631 if(!isset($_SESSION['selected_tags']))
632 $_SESSION['selected_tags'] = Array();
634 if(isset($_SESSION['searchfor']))
635 unset($_SESSION['searchfor']);
637 if(!in_array($tag, $_SESSION['selected_tags']))
638 array_push($_SESSION['selected_tags'], $tag);
643 * remove tag to users session variable
645 * this function removes the specified tag from
646 * users current tag selection
648 public function delTag($tag)
650 if(isset($_SESSION['searchfor']))
651 unset($_SESSION['searchfor']);
653 if(isset($_SESSION['selected_tags'])) {
654 $key = array_search($tag, $_SESSION['selected_tags']);
655 unset($_SESSION['selected_tags'][$key]);
656 sort($_SESSION['selected_tags']);
662 * reset tag selection
664 * if there is any tag selection, it will be
667 public function resetTags()
669 if(isset($_SESSION['selected_tags']))
670 unset($_SESSION['selected_tags']);
677 * if a specific photo was requested (external link)
678 * unset the session variable now
680 public function resetPhotoView()
682 if(isset($_SESSION['current_photo']))
683 unset($_SESSION['current_photo']);
685 } // resetPhotoView();
690 * if any tag search has taken place, reset
693 public function resetTagSearch()
695 if(isset($_SESSION['searchfor']))
696 unset($_SESSION['searchfor']);
698 } // resetTagSearch()
703 * if any date search has taken place, reset
706 public function resetDateSearch()
708 if(isset($_SESSION['from_date']))
709 unset($_SESSION['from_date']);
710 if(isset($_SESSION['to_date']))
711 unset($_SESSION['to_date']);
713 } // resetDateSearch();
716 * return all photo according selection
718 * this function returns all photos based on
719 * the tag-selection, tag- or date-search.
720 * the tag-search also has to take care of AND
721 * and OR conjunctions
723 public function getPhotoSelection()
725 $matched_photos = Array();
727 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
728 $from_date = $_SESSION['from_date'];
729 $to_date = $_SESSION['to_date'];
730 $additional_where_cond = "
731 p.time>='". $from_date ."'
733 p.time<='". $to_date ."'
737 if(isset($_SESSION['sort_order'])) {
738 $order_str = $this->get_sort_order();
741 /* return a search result */
742 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
744 SELECT DISTINCT pt1.photo_id
746 INNER JOIN photo_tags pt2
747 ON pt1.photo_id=pt2.photo_id
754 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
756 if(isset($additional_where_cond))
757 $query_str.= "AND ". $additional_where_cond ." ";
759 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
760 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
763 if(isset($order_str))
764 $query_str.= $order_str;
766 $result = $this->db->db_query($query_str);
767 while($row = $this->db->db_fetch_object($result)) {
768 array_push($matched_photos, $row['photo_id']);
770 return $matched_photos;
773 /* return according the selected tags */
774 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
776 foreach($_SESSION['selected_tags'] as $tag)
777 $selected.= $tag .",";
778 $selected = substr($selected, 0, strlen($selected)-1);
780 /* photo has to match at least on of the selected tags */
781 if($_SESSION['tag_condition'] == 'or') {
783 SELECT DISTINCT pt1.photo_id
785 INNER JOIN photo_tags pt2
786 ON pt1.photo_id=pt2.photo_id
791 WHERE pt1.tag_id IN (". $selected .")
793 if(isset($additional_where_cond))
794 $query_str.= "AND ". $additional_where_cond ." ";
796 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
797 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
800 if(isset($order_str))
801 $query_str.= $order_str;
803 /* photo has to match all selected tags */
804 elseif($_SESSION['tag_condition'] == 'and') {
806 if(count($_SESSION['selected_tags']) >= 32) {
807 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
808 print "evaluate your tag selection. Please remove some tags from your selection.\n";
812 /* Join together a table looking like
814 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
816 so the query can quickly return all images matching the
817 selected tags in an AND condition
822 SELECT DISTINCT pt1.photo_id
826 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
833 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
835 INNER JOIN photo_tags pt". ($i+2) ."
836 ON pt1.photo_id=pt". ($i+2) .".photo_id
843 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
844 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
846 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
849 if(isset($additional_where_cond))
850 $query_str.= "AND ". $additional_where_cond;
852 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
853 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
856 if(isset($order_str))
857 $query_str.= $order_str;
861 $result = $this->db->db_query($query_str);
862 while($row = $this->db->db_fetch_object($result)) {
863 array_push($matched_photos, $row['photo_id']);
865 return $matched_photos;
868 /* return all available photos */
872 LEFT JOIN photo_tags pt
878 if(isset($additional_where_cond))
879 $query_str.= "WHERE ". $additional_where_cond ." ";
881 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
882 if(isset($additional_where_cond))
883 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
885 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
888 if(isset($order_str))
889 $query_str.= $order_str;
891 $result = $this->db->db_query($query_str);
892 while($row = $this->db->db_fetch_object($result)) {
893 array_push($matched_photos, $row['id']);
895 return $matched_photos;
897 } // getPhotoSelection()
900 * control HTML ouput for photo index
902 * this function provides all the necessary information
903 * for the photo index template.
905 public function showPhotoIndex()
907 $photos = $this->getPhotoSelection();
909 $count = count($photos);
911 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
912 $anchor = $_SESSION['begin_with'];
914 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
920 elseif($this->cfg->thumbs_per_page > 0) {
922 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
926 $begin_with = $_SESSION['begin_with'];
929 $end_with = $begin_with + $this->cfg->thumbs_per_page;
933 $images[$thumbs] = Array();
934 $img_height[$thumbs] = Array();
935 $img_width[$thumbs] = Array();
936 $img_id[$thumbs] = Array();
937 $img_name[$thumbs] = Array();
938 $img_title = Array();
940 for($i = $begin_with; $i < $end_with; $i++) {
942 if(isset($photos[$i])) {
944 $images[$thumbs] = $photos[$i];
945 $img_id[$thumbs] = $i;
946 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
947 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
949 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
951 if(file_exists($thumb_path)) {
952 $info = getimagesize($thumb_path);
953 $img_width[$thumbs] = $info[0];
954 $img_height[$thumbs] = $info[1];
960 // +1 for for smarty's selection iteration
963 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
964 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
966 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
967 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
968 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
971 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
972 $this->tmpl->assign('tag_result', 1);
975 /* do we have to display the page selector ? */
976 if($this->cfg->thumbs_per_page != 0) {
980 /* calculate the page switchers */
981 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
982 $next_start = $begin_with + $this->cfg->thumbs_per_page;
985 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
986 if($end_with < $count)
987 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
989 $photo_per_page = $this->cfg->thumbs_per_page;
990 $last_page = ceil($count / $photo_per_page);
992 /* get the current selected page */
993 if($begin_with == 0) {
997 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1004 for($i = 1; $i <= $last_page; $i++) {
1006 if($current_page == $i)
1007 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1008 elseif($current_page-1 == $i || $current_page+1 == $i)
1009 $style = "style=\"font-size: 105%;\"";
1010 elseif(($current_page-5 >= $i) && ($i != 1) ||
1011 ($current_page+5 <= $i) && ($i != $last_page))
1012 $style = "style=\"font-size: 75%;\"";
1016 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1019 $select.= ">". $i ."</a> ";
1021 // until 9 pages we show the selector from 1-9
1022 if($last_page <= 9) {
1023 $page_select.= $select;
1026 if($i == 1 /* first page */ ||
1027 $i == $last_page /* last page */ ||
1028 $i == $current_page /* current page */ ||
1029 $i == ceil($last_page * 0.25) /* first quater */ ||
1030 $i == ceil($last_page * 0.5) /* half */ ||
1031 $i == ceil($last_page * 0.75) /* third quater */ ||
1032 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1033 (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 */ ||
1034 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1035 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1037 $page_select.= $select;
1045 $page_select.= "......... ";
1050 /* only show the page selector if we have more then one page */
1052 $this->tmpl->assign('page_selector', $page_select);
1056 $current_tags = $this->getCurrentTags();
1057 $extern_link = "index.php?mode=showpi";
1058 $rss_link = "index.php?mode=rss";
1059 if($current_tags != "") {
1060 $extern_link.= "&tags=". $current_tags;
1061 $rss_link.= "&tags=". $current_tags;
1063 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1064 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1065 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1068 $export_link = "index.php?mode=export";
1069 $slideshow_link = "index.php?mode=slideshow";
1071 $this->tmpl->assign('extern_link', $extern_link);
1072 $this->tmpl->assign('slideshow_link', $slideshow_link);
1073 $this->tmpl->assign('export_link', $export_link);
1074 $this->tmpl->assign('rss_link', $rss_link);
1075 $this->tmpl->assign('count', $count);
1076 $this->tmpl->assign('width', $this->cfg->thumb_width);
1077 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1078 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1079 $this->tmpl->assign('images', $images);
1080 $this->tmpl->assign('img_width', $img_width);
1081 $this->tmpl->assign('img_height', $img_height);
1082 $this->tmpl->assign('img_id', $img_id);
1083 $this->tmpl->assign('img_name', $img_name);
1084 $this->tmpl->assign('img_title', $img_title);
1085 $this->tmpl->assign('thumbs', $thumbs);
1087 $this->tmpl->show("photo_index.tpl");
1090 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1092 } // showPhotoIndex()
1095 * show credit template
1097 public function showCredits()
1099 $this->tmpl->assign('version', $this->cfg->version);
1100 $this->tmpl->assign('product', $this->cfg->product);
1101 $this->tmpl->assign('db_version', $this->dbver);
1102 $this->tmpl->show("credits.tpl");
1107 * create_thumbnails for the requested width
1109 * this function creates image thumbnails of $orig_image
1110 * stored as $thumb_image. It will check if the image is
1111 * in a supported format, if necessary rotate the image
1112 * (based on EXIF orientation meta headers) and re-sizing.
1114 public function create_thumbnail($orig_image, $thumb_image, $width)
1116 if(!file_exists($orig_image)) {
1120 $details = getimagesize($orig_image);
1122 /* check if original photo is a support image type */
1123 if(!$this->checkifImageSupported($details['mime']))
1126 $meta = $this->get_meta_informations($orig_image);
1132 switch($meta['Orientation']) {
1133 case 1: /* top, left */
1134 /* nothing to do */ break;
1135 case 2: /* top, right */
1136 $rotate = 0; $flip_hori = true; break;
1137 case 3: /* bottom, left */
1138 $rotate = 180; break;
1139 case 4: /* bottom, right */
1140 $flip_vert = true; break;
1141 case 5: /* left side, top */
1142 $rotate = 90; $flip_vert = true; break;
1143 case 6: /* right side, top */
1144 $rotate = 90; break;
1145 case 7: /* left side, bottom */
1146 $rotate = 270; $flip_vert = true; break;
1147 case 8: /* right side, bottom */
1148 $rotate = 270; break;
1151 $src_img = @imagecreatefromjpeg($orig_image);
1154 print "Can't load image from ". $orig_image ."\n";
1158 /* grabs the height and width */
1159 $cur_width = imagesx($src_img);
1160 $cur_height = imagesy($src_img);
1162 // If requested width is more then the actual image width,
1163 // do not generate a thumbnail, instead safe the original
1164 // as thumbnail but with lower quality. But if the image
1165 // is to heigh too, then we still have to resize it.
1166 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1167 $result = imagejpeg($src_img, $thumb_image, 75);
1168 imagedestroy($src_img);
1172 // If the image will be rotate because EXIF orientation said so
1173 // 'virtually rotate' the image for further calculations
1174 if($rotate == 90 || $rotate == 270) {
1176 $cur_width = $cur_height;
1180 /* calculates aspect ratio */
1181 $aspect_ratio = $cur_height / $cur_width;
1184 if($aspect_ratio < 1) {
1186 $new_h = abs($new_w * $aspect_ratio);
1188 /* 'virtually' rotate the image and calculate it's ratio */
1189 $tmp_w = $cur_height;
1190 $tmp_h = $cur_width;
1191 /* now get the ratio from the 'rotated' image */
1192 $tmp_ratio = $tmp_h/$tmp_w;
1193 /* now calculate the new dimensions */
1195 $tmp_h = abs($tmp_w * $tmp_ratio);
1197 // now that we know, how high they photo should be, if it
1198 // gets rotated, use this high to scale the image
1200 $new_w = abs($new_h / $aspect_ratio);
1202 // If the image will be rotate because EXIF orientation said so
1203 // now 'virtually rotate' back the image for the image manipulation
1204 if($rotate == 90 || $rotate == 270) {
1211 /* creates new image of that size */
1212 $dst_img = imagecreatetruecolor($new_w, $new_h);
1214 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1216 /* copies resized portion of original image into new image */
1217 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1219 /* needs the image to be flipped horizontal? */
1221 $this->_debug("(FLIP)");
1222 $dst_img = $this->flipImage($dst_img, 'hori');
1224 /* needs the image to be flipped vertical? */
1226 $this->_debug("(FLIP)");
1227 $dst_img = $this->flipImage($dst_img, 'vert');
1231 $this->_debug("(ROTATE)");
1232 $dst_img = $this->rotateImage($dst_img, $rotate);
1235 /* write down new generated file */
1236 $result = imagejpeg($dst_img, $thumb_image, 75);
1238 /* free your mind */
1239 imagedestroy($dst_img);
1240 imagedestroy($src_img);
1242 if($result === false) {
1243 print "Can't write thumbnail ". $thumb_image ."\n";
1249 } // create_thumbnail()
1252 * return all exif meta data from the file
1254 public function get_meta_informations($file)
1256 return exif_read_data($file);
1258 } // get_meta_informations()
1261 * create phpfspot own sqlite database
1263 * this function creates phpfspots own sqlite database
1264 * if it does not exist yet. this own is used to store
1265 * some necessary informations (md5 sum's, ...).
1267 public function check_config_table()
1269 // if the config table doesn't exist yet, create it
1270 if(!$this->cfg_db->db_check_table_exists("images")) {
1271 $this->cfg_db->db_exec("
1272 CREATE TABLE images (
1273 img_idx int primary key,
1279 } // check_config_table
1282 * Generates a thumbnail from photo idx
1284 * This function will generate JPEG thumbnails from provided F-Spot photo
1287 * 1. Check if all thumbnail generations (width) are already in place and
1289 * 2. Check if the md5sum of the original file has changed
1290 * 3. Generate the thumbnails if needed
1292 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1296 $resolutions = Array(
1297 $this->cfg->thumb_width,
1298 $this->cfg->photo_width,
1299 $this->cfg->mini_width,
1302 /* get details from F-Spot's database */
1303 $details = $this->get_photo_details($idx);
1305 /* calculate file MD5 sum */
1306 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1308 if(!file_exists($full_path)) {
1309 $this->_error("File ". $full_path ." does not exist\n");
1313 if(!is_readable($full_path)) {
1314 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1318 $file_md5 = md5_file($full_path);
1320 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1324 foreach($resolutions as $resolution) {
1326 $generate_it = false;
1328 $thumb_sub_path = substr($file_md5, 0, 2);
1329 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1331 if(!file_exists(dirname($thumb_path))) {
1332 mkdir(dirname($thumb_path), 0755);
1335 /* if the thumbnail file doesn't exist, create it */
1336 if(!file_exists($thumb_path)) {
1337 $generate_it = true;
1339 /* if the file hasn't changed there is no need to regen the thumb */
1340 elseif($file_md5 != $this->getMD5($idx) || $force) {
1341 $generate_it = true;
1344 if($generate_it || $overwrite) {
1346 $this->_debug(" ". $resolution ."px");
1347 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1355 $this->_debug(" already exist");
1358 /* set the new/changed MD5 sum for the current photo */
1360 $this->setMD5($idx, $file_md5);
1363 $this->_debug("\n");
1368 * returns stored md5 sum for a specific photo
1370 * this function queries the phpfspot database for a
1371 * stored MD5 checksum of the specified photo
1373 public function getMD5($idx)
1375 $result = $this->cfg_db->db_query("
1378 WHERE img_idx='". $idx ."'
1384 $img = $this->cfg_db->db_fetch_object($result);
1385 return $img['img_md5'];
1390 * set MD5 sum for the specific photo
1392 private function setMD5($idx, $md5)
1394 $result = $this->cfg_db->db_exec("
1395 REPLACE INTO images (img_idx, img_md5)
1396 VALUES ('". $idx ."', '". $md5 ."')
1402 * store current tag condition
1404 * this function stores the current tag condition
1405 * (AND or OR) in the users session variables
1407 public function setTagCondition($mode)
1409 $_SESSION['tag_condition'] = $mode;
1411 } // setTagCondition()
1414 * invoke tag & date search
1416 * this function will return all matching tags and store
1417 * them in the session variable selected_tags. furthermore
1418 * it also handles the date search.
1419 * getPhotoSelection() will then only return the matching
1422 public function startSearch($searchfor, $from = 0, $to = 0)
1426 $_SESSION['searchfor'] = $searchfor;
1429 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1431 unset($_SESSION['from_date']);
1434 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1436 unset($_SESSION['to_date']);
1438 if($searchfor != "") {
1439 /* new search, reset the current selected tags */
1440 $_SESSION['selected_tags'] = Array();
1441 foreach($this->avail_tags as $tag) {
1442 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1443 array_push($_SESSION['selected_tags'], $tag);
1450 * updates sort order in session variable
1452 * this function is invoked by RPC and will sort the requested
1453 * sort order in the session variable.
1455 public function updateSortOrder($order)
1457 if(isset($this->sort_orders[$order])) {
1458 $_SESSION['sort_order'] = $order;
1462 return "unkown error";
1464 } // updateSortOrder()
1469 * this function rotates the image according the
1472 private function rotateImage($img, $degrees)
1474 if(function_exists("imagerotate")) {
1475 $img = imagerotate($img, $degrees, 0);
1477 function imagerotate($src_img, $angle)
1479 $src_x = imagesx($src_img);
1480 $src_y = imagesy($src_img);
1481 if ($angle == 180) {
1485 elseif ($src_x <= $src_y) {
1489 elseif ($src_x >= $src_y) {
1494 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1495 imagealphablending($rotate, false);
1500 for ($y = 0; $y < ($src_y); $y++) {
1501 for ($x = 0; $x < ($src_x); $x++) {
1502 $color = imagecolorat($src_img, $x, $y);
1503 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1509 for ($y = 0; $y < ($src_y); $y++) {
1510 for ($x = 0; $x < ($src_x); $x++) {
1511 $color = imagecolorat($src_img, $x, $y);
1512 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1518 for ($y = 0; $y < ($src_y); $y++) {
1519 for ($x = 0; $x < ($src_x); $x++) {
1520 $color = imagecolorat($src_img, $x, $y);
1521 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1535 $img = imagerotate($img, $degrees);
1544 * returns flipped image
1546 * this function will return an either horizontal or
1547 * vertical flipped truecolor image.
1549 private function flipImage($image, $mode)
1551 $w = imagesx($image);
1552 $h = imagesy($image);
1553 $flipped = imagecreatetruecolor($w, $h);
1557 for ($y = 0; $y < $h; $y++) {
1558 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1562 for ($x = 0; $x < $w; $x++) {
1563 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1573 * return all assigned tags for the specified photo
1575 private function get_photo_tags($idx)
1577 $result = $this->db->db_query("
1580 INNER JOIN photo_tags pt
1582 WHERE pt.photo_id='". $idx ."'
1587 while($row = $this->db->db_fetch_object($result))
1588 $tags[$row['id']] = $row['name'];
1592 } // get_photo_tags()
1595 * create on-the-fly images with text within
1597 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1599 if (strlen($color) != 6)
1602 $int = hexdec($color);
1603 $h = imagefontheight($font);
1604 $fw = imagefontwidth($font);
1605 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1606 $lines = count($txt);
1607 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1608 $bg = imagecolorallocate($im, 255, 255, 255);
1609 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1612 foreach ($txt as $text) {
1613 $x = (($w - ($fw * strlen($text))) / 2);
1614 imagestring($im, $font, $x, $y, $text, $color);
1615 $y += ($h + $space);
1618 Header("Content-type: image/png");
1621 } // showTextImage()
1624 * check if all requirements are met
1626 private function checkRequirements()
1628 if(!function_exists("imagecreatefromjpeg")) {
1629 print "PHP GD library extension is missing<br />\n";
1633 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1634 print "PHP SQLite3 library extension is missing<br />\n";
1638 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1639 ini_set('track_errors', 1);
1640 @include_once 'HTML/AJAX/Server.php';
1641 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1642 print "PEAR HTML_AJAX package is missing<br />\n";
1645 @include_once 'Calendar/Calendar.php';
1646 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1647 print "PEAR Calendar package is missing<br />\n";
1650 @include_once 'Console/Getopt.php';
1651 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1652 print "PEAR Console_Getopt package is missing<br />\n";
1655 ini_restore('track_errors');
1662 } // checkRequirements()
1664 private function _debug($text)
1666 if($this->fromcmd) {
1673 * check if specified MIME type is supported
1675 public function checkifImageSupported($mime)
1677 if(in_array($mime, Array("image/jpeg")))
1682 } // checkifImageSupported()
1684 public function _error($text)
1686 switch($this->cfg->logging) {
1688 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1695 error_log($text, 3, $his->cfg->log_file);
1702 * output calendard input fields
1704 private function get_calendar($mode)
1706 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1707 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1708 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1710 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1711 if(!isset($_SESSION[$mode .'_date']))
1712 $output.= " disabled=\"disabled\"";
1714 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1715 if(!isset($_SESSION[$mode .'_date']))
1716 $output.= " disabled=\"disabled\"";
1718 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1719 if(!isset($_SESSION[$mode .'_date']))
1720 $output.= " disabled=\"disabled\"";
1728 * output calendar matrix
1730 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1732 if (!isset($year)) $year = date('Y');
1733 if (!isset($month)) $month = date('m');
1734 if (!isset($day)) $day = date('d');
1739 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1740 require_once CALENDAR_ROOT.'Day.php';
1743 $month = new Calendar_Month_Weekdays($year,$month);
1746 $prevStamp = $month->prevMonth(true);
1747 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1748 $nextStamp = $month->nextMonth(true);
1749 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1751 $selectedDays = array (
1752 new Calendar_Day($year,$month,$day),
1753 new Calendar_Day($year,12,25),
1756 // Build the days in the month
1757 $month->build($selectedDays);
1759 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1760 $this->tmpl->assign('prev_month', $prev);
1761 $this->tmpl->assign('next_month', $next);
1763 while ( $day = $month->fetch() ) {
1765 if(!isset($matrix[$rows]))
1766 $matrix[$rows] = Array();
1770 $dayStamp = $day->thisDay(true);
1771 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1773 // isFirst() to find start of week
1774 if ( $day->isFirst() )
1777 if ( $day->isSelected() ) {
1778 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1779 } else if ( $day->isEmpty() ) {
1780 $string.= "<td> </td>\n";
1782 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1785 // isLast() to find end of week
1786 if ( $day->isLast() )
1787 $string.= "</tr>\n";
1789 $matrix[$rows][$cols] = $string;
1799 $this->tmpl->assign('matrix', $matrix);
1800 $this->tmpl->assign('rows', $rows);
1801 $this->tmpl->show("calendar.tpl");
1803 } // get_calendar_matrix()
1806 * output export page
1808 public function getExport($mode)
1810 $pictures = $this->getPhotoSelection();
1811 $current_tags = $this->getCurrentTags();
1813 foreach($pictures as $picture) {
1815 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1816 if($current_tags != "") {
1817 $orig_url.= "&tags=". $current_tags;
1819 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1820 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1823 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1828 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1829 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1833 // "[%pictureurl% %thumbnailurl%]"
1834 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1837 case 'MoinMoinList':
1838 // " * [%pictureurl% %thumbnailurl%]"
1839 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1850 public function getRSSFeed()
1852 Header("Content-type: text/xml; charset=utf-8");
1853 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1856 xmlns:media="http://search.yahoo.com/mrss/"
1857 xmlns:dc="http://purl.org/dc/elements/1.1/"
1860 <title>phpfspot</title>
1861 <description>phpfspot RSS feed</description>
1862 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1863 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1864 <generator>phpfspot</generator>
1867 $pictures = $this->getPhotoSelection();
1868 $current_tags = $this->getCurrentTags();
1870 foreach($pictures as $picture) {
1872 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1873 if($current_tags != "") {
1874 $orig_url.= "&tags=". $current_tags;
1876 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1877 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1880 $details = $this->get_photo_details($picture);
1882 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1883 $thumb_html = htmlspecialchars("
1884 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1886 ". $details['description']);
1888 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1889 $meta = $this->get_meta_informations($orig_path);
1890 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1894 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1895 <link><?php print htmlspecialchars($orig_url); ?></link>
1896 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1897 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1899 <?php print $thumb_html; ?>
1901 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1916 * return all selected tags as one string
1918 private function getCurrentTags()
1921 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1922 foreach($_SESSION['selected_tags'] as $tag)
1923 $current_tags.= $tag .",";
1924 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1926 return $current_tags;
1928 } // getCurrentTags()
1931 * return the current photo
1933 public function getCurrentPhoto()
1935 if(isset($_SESSION['current_photo'])) {
1936 print $_SESSION['current_photo'];
1938 } // getCurrentPhoto()
1941 * tells the client browser what to do
1943 * this function is getting called via AJAX by the
1944 * client browsers. it will tell them what they have
1945 * to do next. This is necessary for directly jumping
1946 * into photo index or single photo view when the are
1947 * requested with specific URLs
1949 public function whatToDo()
1951 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1952 return "show_photo";
1954 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1955 return "showpi_tags";
1957 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1961 return "nothing special";
1966 * return the current process-user
1968 private function getuid()
1970 if($uid = posix_getuid()) {
1971 if($user = posix_getpwuid($uid)) {
1972 return $user['name'];
1981 * returns a select-dropdown box to select photo index sort parameters
1983 public function smarty_sort_select_list($params, &$smarty)
1987 foreach($this->sort_orders as $key => $value) {
1988 $output.= "<option value=\"". $key ."\"";
1989 if($key == $_SESSION['sort_order']) {
1990 $output.= " selected=\"selected\"";
1992 $output.= ">". $value ."</option>";
1997 } // smarty_sort_select_list()
2000 * returns the currently selected sort order
2002 private function get_sort_order()
2004 switch($_SESSION['sort_order']) {
2006 return " ORDER BY p.time ASC";
2009 return " ORDER BY p.time DESC";
2012 if($this->dbver < 9) {
2013 return " ORDER BY p.name ASC";
2016 return " ORDER BY basename(p.uri) ASC";
2020 if($this->dbver < 9) {
2021 return " ORDER BY p.name DESC";
2024 return " ORDER BY basename(p.uri) DESC";
2028 return " ORDER BY t.name ASC ,p.time ASC";
2031 return " ORDER BY t.name DESC ,p.time ASC";
2035 } // get_sort_order()
2038 * return the next to be shown slide show image
2040 * this function returns the URL of the next image
2041 * in the slideshow sequence.
2043 public function getNextSlideShowImage()
2045 $all_photos = $this->getPhotoSelection();
2047 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2048 $_SESSION['slideshow_img'] = 0;
2050 $_SESSION['slideshow_img']++;
2052 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2054 } // getNextSlideShowImage()
2057 * return the previous to be shown slide show image
2059 * this function returns the URL of the previous image
2060 * in the slideshow sequence.
2062 public function getPrevSlideShowImage()
2064 $all_photos = $this->getPhotoSelection();
2066 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2067 $_SESSION['slideshow_img'] = 0;
2069 $_SESSION['slideshow_img']--;
2071 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2073 } // getPrevSlideShowImage()
2075 public function resetSlideShow()
2077 if(isset($_SESSION['slideshow_img']))
2078 unset($_SESSION['slideshow_img']);
2079 } // resetSlideShow()
2084 * this function will get all photos from the fspot
2085 * database and randomly return ONE entry
2087 * saddly there is yet no sqlite3 function which returns
2088 * the bulk result in array, so we have to fill up our
2091 public function get_random_photo()
2095 $result = $this->db->db_query("
2100 while($row = $this->db->db_fetch_object($result)) {
2101 array_push($all, $row['id']);
2104 return $all[array_rand($all)];
2106 } // get_random_photo()
2109 * validates provided date
2111 * this function validates if the provided date
2112 * contains a valid date and will return true
2115 public function isValidDate($date_str)
2117 $timestamp = strtotime($date_str);
2119 if(is_numeric($timestamp))
2127 * timestamp to string conversion
2129 private function ts2str($timestamp)
2131 return strftime("%Y-%m-%d", $timestamp);
2134 private function extractTags($tags_str)
2136 $not_validated = split(',', $_GET['tags']);
2137 $validated = array();
2139 foreach($not_validated as $tag) {
2140 if(is_numeric($tag))
2141 array_push($validated, $tag);
2149 * returns the full path to a thumbnail
2151 public function get_thumb_path($width, $photo)
2153 $md5 = $this->getMD5($photo);
2154 $sub_path = substr($md5, 0, 2);
2155 return $this->cfg->thumb_path
2163 } // get_thumb_path()
2166 * returns server's virtual host name
2168 private function get_server_name()
2170 return $_SERVER['SERVER_NAME'];
2171 } // get_server_name()
2174 * returns type of webprotocol which is
2177 private function get_web_protocol()
2179 if(!isset($_SERVER['HTTPS']))
2183 } // get_web_protocol()
2186 * return url to this phpfspot installation
2188 private function get_phpfspot_url()
2190 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2191 } // get_phpfspot_url()
2194 * check file exists and is readable
2196 * returns true, if everything is ok, otherwise false
2197 * if $silent is not set, this function will output and
2200 private function check_readable($file, $silent = null)
2202 if(!file_exists($file)) {
2204 print "File \"". $file ."\" does not exist.\n";
2208 if(!is_readable($file)) {
2210 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2216 } // check_readable()
2219 * check if all needed indices are present
2221 * this function checks, if some needed indices are already
2222 * present, or if not, create them on the fly. they are
2223 * necessary to speed up some queries like that one look for
2224 * all tags, when show_tags is specified in the configuration.
2226 private function checkDbIndices()
2228 $result = $this->db->db_exec("
2229 CREATE INDEX IF NOT EXISTS
2236 } // checkDbIndices()
2239 * retrive F-Spot database version
2241 * this function will return the F-Spot database version number
2242 * It is stored within the sqlite3 database in the table meta
2244 public function getFspotDBVersion()
2246 if($result = $this->db->db_fetchSingleRow("
2247 SELECT data as version
2250 name LIKE 'F-Spot Database Version'
2252 return $result['version'];
2256 } // getFspotDBVersion()
2259 * parse the provided URI and will returned the
2262 public function parse_uri($uri, $mode)
2264 if(($components = parse_url($uri)) !== false) {
2268 return basename($components['path']);
2271 return dirname($components['path']);
2274 return $components['path'];