3 /***************************************************************************
5 * Copyright (c) by Andreas Unterkircher, unki@netshadow.at
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 ***************************************************************************/
24 require_once "phpfspot_cfg.php";
25 require_once "phpfspot_db.php";
40 * this function will be called on class construct
41 * and will check requirements, loads configuration,
42 * open databases and start the user session
44 public function __construct()
46 $this->cfg = new PHPFSPOT_CFG;
48 /* set application name and version information */
49 $this->cfg->product = "phpfspot";
50 $this->cfg->version = "1.2";
52 /* Check necessary requirements */
53 if(!$this->checkRequirements()) {
57 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
58 if(!is_writeable($this->cfg->fspot_db)) {
59 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
63 $this->dbver = $this->getFspotDBVersion();
65 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
66 print dirname($this->cfg->phpfspot_db) .": directory is not writeable!";
70 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
71 if(!is_writeable($this->cfg->phpfspot_db)) {
72 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
75 $this->check_config_table();
77 /* include Smarty template engine */
78 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
81 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
82 /* overload Smarty class if our own template handler */
83 require_once "phpfspot_tmpl.php";
84 $this->tmpl = new PHPFSPOT_TMPL($this);
86 /* check if all necessary indices exist */
87 $this->checkDbIndices();
91 if(!isset($_SESSION['tag_condition']))
92 $_SESSION['tag_condition'] = 'or';
94 if(!isset($_SESSION['sort_order']))
95 $_SESSION['sort_order'] = 'date_asc';
97 if(!isset($_SESSION['searchfor']))
98 $_SESSION['searchfor'] = '';
100 // if begin_with is still set but rows_per_page is now 0, unset it
101 if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
102 unset($_SESSION['begin_with']);
106 public function __destruct()
112 * show - generate html output
114 * this function can be called after the constructor has
115 * prepared everyhing. it will load the index.tpl smarty
116 * template. if necessary it will registere pre-selects
117 * (photo index, photo, tag search, date search) into
120 public function show()
122 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
123 $this->tmpl->assign('page_title', $this->cfg->page_title);
124 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
125 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
127 if(isset($_GET['mode'])) {
129 $_SESSION['start_action'] = $_GET['mode'];
131 switch($_GET['mode']) {
133 if(isset($_GET['tags'])) {
134 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
136 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
137 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
139 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
140 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
144 if(isset($_GET['tags'])) {
145 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
146 $_SESSION['start_action'] = 'showp';
148 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
149 $_SESSION['current_photo'] = $_GET['id'];
150 $_SESSION['start_action'] = 'showp';
152 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
153 $_SESSION['from_date'] = strtotime($_GET['from_date']);
155 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
156 $_SESSION['to_date'] = strtotime($_GET['to_date']);
160 $this->tmpl->show("export.tpl");
164 $this->tmpl->show("slideshow.tpl");
168 if(isset($_GET['tags'])) {
169 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
171 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
172 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
174 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
175 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
183 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
184 $this->tmpl->assign('date_search_enabled', true);
186 $this->tmpl->assign('from_date', $this->get_calendar('from'));
187 $this->tmpl->assign('to_date', $this->get_calendar('to'));
188 $this->tmpl->assign('sort_field', $this->get_sort_field());
189 $this->tmpl->assign('content_page', 'welcome.tpl');
190 $this->tmpl->show("index.tpl");
195 * get_tags - grab all tags of f-spot's database
197 * this function will get all available tags from
198 * the f-spot database and store them within two
199 * arrays within this class for later usage. in
200 * fact, if the user requests (hide_tags) it will
201 * opt-out some of them.
203 * this function is getting called once by show()
205 private function get_tags()
207 $this->avail_tags = Array();
210 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
213 DISTINCT t1.id as id, t1.name as name
216 INNER JOIN photo_tags
217 pt2 ON pt1.photo_id=pt2.photo_id
223 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
225 t1.sort_priority ASC";
227 $result = $this->db->db_query($query_str);
231 $result = $this->db->db_query("
234 ORDER BY sort_priority ASC
238 while($row = $this->db->db_fetch_object($result)) {
240 $tag_id = $row['id'];
241 $tag_name = $row['name'];
243 /* if the user has specified to ignore this tag in phpfspot's
244 configuration, ignore it here so it does not get added to
247 if(in_array($row['name'], $this->cfg->hide_tags))
250 /* if you include the following if-clause and the user has specified
251 to only show certain tags which are specified in phpfspot's
252 configuration, ignore all others so they will not be added to the
254 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
255 !in_array($row['name'], $this->cfg->show_tags))
259 $this->tags[$tag_id] = $tag_name;
260 $this->avail_tags[$count] = $tag_id;
268 * extract all photo details
270 * retrieve all available details from f-spot's
271 * database and return them as object
273 public function get_photo_details($idx)
275 if($this->dbver < 9) {
277 SELECT p.id, p.name, p.time, p.directory_path, p.description
283 SELECT p.id, p.uri, p.time, p.description
288 /* if show_tags is set, only return details for photos which
289 are specified to be shown
291 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
293 INNER JOIN photo_tags pt
297 WHERE p.id='". $idx ."'
298 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
302 WHERE p.id='". $idx ."'
306 if($result = $this->db->db_query($query_str)) {
308 $row = $this->db->db_fetch_object($result);
310 if($this->dbver < 9) {
311 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
320 } // get_photo_details
323 * returns aligned photo names
325 * this function returns aligned (length) names for
326 * an specific photo. If the length of the name exceeds
327 * $limit the name will be shrinked (...)
329 public function getPhotoName($idx, $limit = 0)
331 if($details = $this->get_photo_details($idx)) {
332 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
333 $name = $this->shrink_text($long_name, $limit);
343 * shrink text according provided limit
345 * If the length of the name exceeds $limit the
346 * text will be shortend and some content in between
347 * will be replaced with "..."
349 private function shrink_text($text, $limit)
351 if($limit != 0 && strlen($text) > $limit) {
352 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
360 * translate f-spoth photo path
362 * as the full-qualified path recorded in the f-spot database
363 * is usally not the same as on the webserver, this function
364 * will replace the path with that one specified in the cfg
366 public function translate_path($path, $width = 0)
368 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
373 * control HTML ouput for a single photo
375 * this function provides all the necessary information
376 * for the single photo template.
378 public function showPhoto($photo)
380 /* get all photos from the current photo selection */
381 $all_photos = $this->getPhotoSelection();
382 $count = count($all_photos);
384 for($i = 0; $i < $count; $i++) {
386 // $get_next will be set, when the photo which has to
387 // be displayed has been found - this means that the
388 // next available is in fact the NEXT image (for the
390 if(isset($get_next)) {
391 $next_img = $all_photos[$i];
395 /* the next photo is our NEXT photo */
396 if($all_photos[$i] == $photo) {
400 $previous_img = $all_photos[$i];
403 if($photo == $all_photos[$i]) {
408 $details = $this->get_photo_details($photo);
415 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
416 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
418 if(!file_exists($orig_path)) {
419 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
422 if(!is_readable($orig_path)) {
423 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
426 /* If the thumbnail doesn't exist yet, try to create it */
427 if(!file_exists($thumb_path)) {
428 $this->gen_thumb($photo, true);
429 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
432 /* get f-spot database meta information */
433 $meta = $this->get_meta_informations($orig_path);
435 /* If EXIF data are available, use them */
436 if(isset($meta['ExifImageWidth'])) {
437 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
439 $info = getimagesize($orig_path);
440 $meta_res = $info[0] ."x". $info[1];
443 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
444 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
445 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
447 $extern_link = "index.php?mode=showp&id=". $photo;
448 $current_tags = $this->getCurrentTags();
449 if($current_tags != "") {
450 $extern_link.= "&tags=". $current_tags;
452 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
453 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
456 $this->tmpl->assign('extern_link', $extern_link);
458 if(file_exists($thumb_path)) {
460 $info = getimagesize($thumb_path);
462 $this->tmpl->assign('description', $details['description']);
463 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
465 $this->tmpl->assign('width', $info[0]);
466 $this->tmpl->assign('height', $info[1]);
467 $this->tmpl->assign('ExifMadeOn', $meta_date);
468 $this->tmpl->assign('ExifMadeWith', $meta_make);
469 $this->tmpl->assign('ExifOrigResolution', $meta_res);
470 $this->tmpl->assign('ExifFileSize', $meta_size);
472 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
473 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
475 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
476 $this->tmpl->assign('current', $current);
479 $this->_error("Can't open file ". $thumb_path ."\n");
484 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
485 $this->tmpl->assign('prev_img', $previous_img);
489 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
490 $this->tmpl->assign('next_img', $next_img);
492 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
493 $this->tmpl->assign('photo_number', $i);
494 $this->tmpl->assign('photo_count', count($all_photos));
496 $this->tmpl->show("single_photo.tpl");
501 * all available tags and tag cloud
503 * this function outputs all available tags (time ordered)
504 * and in addition output them as tag cloud (tags which have
505 * many photos will appears more then others)
507 public function getAvailableTags()
513 $result = $this->db->db_query("
514 SELECT tag_id as id, count(tag_id) as quantity
524 while($row = $this->db->db_fetch_object($result)) {
525 $tags[$row['id']] = $row['quantity'];
528 // change these font sizes if you will
529 $max_size = 125; // max font size in %
530 $min_size = 75; // min font size in %
532 // get the largest and smallest array values
533 $max_qty = max(array_values($tags));
534 $min_qty = min(array_values($tags));
536 // find the range of values
537 $spread = $max_qty - $min_qty;
538 if (0 == $spread) { // we don't want to divide by zero
542 // determine the font-size increment
543 // this is the increase per tag quantity (times used)
544 $step = ($max_size - $min_size)/($spread);
546 // loop through our tag array
547 foreach ($tags as $key => $value) {
549 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
552 // calculate CSS font-size
553 // find the $value in excess of $min_qty
554 // multiply by the font-size increment ($size)
555 // and add the $min_size set above
556 $size = $min_size + (($value - $min_qty) * $step);
557 // uncomment if you want sizes in whole %:
560 if(isset($this->tags[$key])) {
561 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
566 $output = substr($output, 0, strlen($output)-2);
569 } // getAvailableTags()
572 * output all selected tags
574 * this function output all tags which have been selected
575 * by the user. the selected tags are stored in the
576 * session-variable $_SESSION['selected_tags']
578 public function getSelectedTags()
583 foreach($this->avail_tags as $tag)
585 // return all selected tags
586 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
587 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
591 $output = substr($output, 0, strlen($output)-2);
594 } // getSelectedTags()
597 * add tag to users session variable
599 * this function will add the specified to users current
600 * tag selection. if a date search has been made before
601 * it will be now cleared
603 public function addTag($tag)
605 if(!isset($_SESSION['selected_tags']))
606 $_SESSION['selected_tags'] = Array();
608 if(!in_array($tag, $_SESSION['selected_tags']))
609 array_push($_SESSION['selected_tags'], $tag);
614 * remove tag to users session variable
616 * this function removes the specified tag from
617 * users current tag selection
619 public function delTag($tag)
621 if(isset($_SESSION['selected_tags'])) {
622 $key = array_search($tag, $_SESSION['selected_tags']);
623 unset($_SESSION['selected_tags'][$key]);
624 sort($_SESSION['selected_tags']);
630 * reset tag selection
632 * if there is any tag selection, it will be
635 public function resetTags()
637 if(isset($_SESSION['selected_tags']))
638 unset($_SESSION['selected_tags']);
645 * if a specific photo was requested (external link)
646 * unset the session variable now
648 public function resetPhotoView()
650 if(isset($_SESSION['current_photo']))
651 unset($_SESSION['current_photo']);
653 } // resetPhotoView();
658 * if any tag search has taken place, reset
661 public function resetTagSearch()
663 if(isset($_SESSION['searchfor']))
664 unset($_SESSION['searchfor']);
666 } // resetTagSearch()
671 * if any date search has taken place, reset
674 public function resetDateSearch()
676 if(isset($_SESSION['from_date']))
677 unset($_SESSION['from_date']);
678 if(isset($_SESSION['to_date']))
679 unset($_SESSION['to_date']);
681 } // resetDateSearch();
684 * return all photo according selection
686 * this function returns all photos based on
687 * the tag-selection, tag- or date-search.
688 * the tag-search also has to take care of AND
689 * and OR conjunctions
691 public function getPhotoSelection()
693 $matched_photos = Array();
695 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
696 $from_date = $_SESSION['from_date'];
697 $to_date = $_SESSION['to_date'];
698 $additional_where_cond = "
699 p.time>='". $from_date ."'
701 p.time<='". $to_date ."'
705 if(isset($_SESSION['sort_order'])) {
706 $order_str = $this->get_sort_order();
709 /* return a search result */
710 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
712 SELECT DISTINCT pt1.photo_id
714 INNER JOIN photo_tags pt2
715 ON pt1.photo_id=pt2.photo_id
722 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
724 if(isset($additional_where_cond))
725 $query_str.= "AND ". $additional_where_cond ." ";
727 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
728 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
731 if(isset($order_str))
732 $query_str.= $order_str;
734 $result = $this->db->db_query($query_str);
735 while($row = $this->db->db_fetch_object($result)) {
736 array_push($matched_photos, $row['photo_id']);
738 return $matched_photos;
741 /* return according the selected tags */
742 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
744 foreach($_SESSION['selected_tags'] as $tag)
745 $selected.= $tag .",";
746 $selected = substr($selected, 0, strlen($selected)-1);
748 /* photo has to match at least on of the selected tags */
749 if($_SESSION['tag_condition'] == 'or') {
751 SELECT DISTINCT pt1.photo_id
753 INNER JOIN photo_tags pt2
754 ON pt1.photo_id=pt2.photo_id
759 WHERE pt1.tag_id IN (". $selected .")
761 if(isset($additional_where_cond))
762 $query_str.= "AND ". $additional_where_cond ." ";
764 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
765 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
768 if(isset($order_str))
769 $query_str.= $order_str;
771 /* photo has to match all selected tags */
772 elseif($_SESSION['tag_condition'] == 'and') {
774 if(count($_SESSION['selected_tags']) >= 32) {
775 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
776 print "evaluate your tag selection. Please remove some tags from your selection.\n";
780 /* Join together a table looking like
782 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
784 so the query can quickly return all images matching the
785 selected tags in an AND condition
790 SELECT DISTINCT pt1.photo_id
794 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
801 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
803 INNER JOIN photo_tags pt". ($i+2) ."
804 ON pt1.photo_id=pt". ($i+2) .".photo_id
811 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
812 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
814 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
817 if(isset($additional_where_cond))
818 $query_str.= "AND ". $additional_where_cond;
820 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
821 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
824 if(isset($order_str))
825 $query_str.= $order_str;
829 $result = $this->db->db_query($query_str);
830 while($row = $this->db->db_fetch_object($result)) {
831 array_push($matched_photos, $row['photo_id']);
833 return $matched_photos;
836 /* return all available photos */
838 SELECT DISTINCT photo_id
845 if(isset($additional_where_cond))
846 $query_str.= "WHERE ". $additional_where_cond ." ";
848 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
849 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
852 if(isset($order_str))
853 $query_str.= $order_str;
855 $result = $this->db->db_query($query_str);
856 while($row = $this->db->db_fetch_object($result)) {
857 array_push($matched_photos, $row['photo_id']);
859 return $matched_photos;
861 } // getPhotoSelection()
864 * control HTML ouput for photo index
866 * this function provides all the necessary information
867 * for the photo index template.
869 public function showPhotoIndex()
871 $photos = $this->getPhotoSelection();
873 $count = count($photos);
875 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
876 $anchor = $_SESSION['begin_with'];
878 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
884 elseif($this->cfg->rows_per_page > 0) {
886 if(!$_SESSION['begin_with'] || $_SESSION['begin_with'] == 0)
890 $begin_with = $_SESSION['begin_with'];
892 // verify $begin_with - perhaps the thumbs-per-rows or
893 // rows-per-page variables have changed or the jump back
894 // from a photo wasn't exact - so calculate the real new
896 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
897 for($i = 0; $i <= $count; $i+=$multiplicator) {
898 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
905 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
911 $images[$rows] = Array();
912 $img_height[$rows] = Array();
913 $img_width[$rows] = Array();
914 $img_id[$rows] = Array();
915 $img_name[$rows] = Array();
916 $img_title = Array();
918 for($i = $begin_with; $i < $end_with; $i++) {
920 $images[$rows][$cols] = $photos[$i];
921 $img_id[$rows][$cols] = $i;
922 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
923 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
925 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
927 if(file_exists($thumb_path)) {
928 $info = getimagesize($thumb_path);
929 $img_width[$rows][$cols] = $info[0];
930 $img_height[$rows][$cols] = $info[1];
933 if($cols == $this->cfg->thumbs_per_row-1) {
936 $images[$rows] = Array();
937 $img_width[$rows] = Array();
938 $img_height[$rows] = Array();
945 // +1 for for smarty's selection iteration
948 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
949 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
951 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
952 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
953 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
956 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
957 $this->tmpl->assign('tag_result', 1);
960 /* do we have to display the page selector ? */
961 if($this->cfg->rows_per_page != 0) {
963 /* calculate the page switchers */
964 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
965 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
968 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
969 if($end_with < $count)
970 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
972 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
973 $last_page = ceil($count / $photo_per_page);
975 /* get the current selected page */
976 if($begin_with == 0) {
980 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
987 for($i = 1; $i <= $last_page; $i++) {
989 if($current_page == $i)
990 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
991 elseif($current_page-1 == $i || $current_page+1 == $i)
992 $style = "style=\"font-size: 105%;\"";
993 elseif(($current_page-5 >= $i) && ($i != 1) ||
994 ($current_page+5 <= $i) && ($i != $last_page))
995 $style = "style=\"font-size: 75%;\"";
999 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1002 $select.= ">". $i ."</a> ";
1004 // until 9 pages we show the selector from 1-9
1005 if($last_page <= 9) {
1006 $page_select.= $select;
1009 if($i == 1 /* first page */ ||
1010 $i == $last_page /* last page */ ||
1011 $i == $current_page /* current page */ ||
1012 $i == ceil($last_page * 0.25) /* first quater */ ||
1013 $i == ceil($last_page * 0.5) /* half */ ||
1014 $i == ceil($last_page * 0.75) /* third quater */ ||
1015 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1016 (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 */ ||
1017 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1018 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1020 $page_select.= $select;
1028 $page_select.= "......... ";
1033 /* only show the page selector if we have more then one page */
1035 $this->tmpl->assign('page_selector', $page_select);
1039 $current_tags = $this->getCurrentTags();
1040 $extern_link = "index.php?mode=showpi";
1041 $rss_link = "index.php?mode=rss";
1042 if($current_tags != "") {
1043 $extern_link.= "&tags=". $current_tags;
1044 $rss_link.= "&tags=". $current_tags;
1046 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1047 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1048 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1051 $export_link = "index.php?mode=export";
1052 $slideshow_link = "index.php?mode=slideshow";
1054 $this->tmpl->assign('extern_link', $extern_link);
1055 $this->tmpl->assign('slideshow_link', $slideshow_link);
1056 $this->tmpl->assign('export_link', $export_link);
1057 $this->tmpl->assign('rss_link', $rss_link);
1058 $this->tmpl->assign('count', $count);
1059 $this->tmpl->assign('width', $this->cfg->thumb_width);
1060 $this->tmpl->assign('images', $images);
1061 $this->tmpl->assign('img_width', $img_width);
1062 $this->tmpl->assign('img_height', $img_height);
1063 $this->tmpl->assign('img_id', $img_id);
1064 $this->tmpl->assign('img_name', $img_name);
1065 $this->tmpl->assign('img_title', $img_title);
1066 $this->tmpl->assign('rows', $rows);
1067 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1069 $this->tmpl->show("photo_index.tpl");
1072 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1074 } // showPhotoIndex()
1077 * show credit template
1079 public function showCredits()
1081 $this->tmpl->assign('version', $this->cfg->version);
1082 $this->tmpl->assign('product', $this->cfg->product);
1083 $this->tmpl->assign('db_version', $this->dbver);
1084 $this->tmpl->show("credits.tpl");
1089 * create_thumbnails for the requested width
1091 * this function creates image thumbnails of $orig_image
1092 * stored as $thumb_image. It will check if the image is
1093 * in a supported format, if necessary rotate the image
1094 * (based on EXIF orientation meta headers) and re-sizing.
1096 public function create_thumbnail($orig_image, $thumb_image, $width)
1098 if(!file_exists($orig_image)) {
1102 $details = getimagesize($orig_image);
1104 /* check if original photo is a support image type */
1105 if(!$this->checkifImageSupported($details['mime']))
1108 $meta = $this->get_meta_informations($orig_image);
1113 switch($meta['Orientation']) {
1115 case 1: /* top, left */
1116 $rotate = 0; $flip = false; break;
1117 case 2: /* top, right */
1118 $rotate = 0; $flip = true; break;
1119 case 3: /* bottom, left */
1120 $rotate = 180; $flip = false; break;
1121 case 4: /* bottom, right */
1122 $rotate = 180; $flip = true; break;
1123 case 5: /* left side, top */
1124 $rotate = 90; $flip = true; break;
1125 case 6: /* right side, top */
1126 $rotate = 90; $flip = false; break;
1127 case 7: /* left side, bottom */
1128 $rotate = 270; $flip = true; break;
1129 case 8: /* right side, bottom */
1130 $rotate = 270; $flip = false; break;
1133 $src_img = @imagecreatefromjpeg($orig_image);
1136 print "Can't load image from ". $orig_image ."\n";
1140 /* grabs the height and width */
1141 $cur_width = imagesx($src_img);
1142 $cur_height = imagesy($src_img);
1144 // If requested width is more then the actual image width,
1145 // do not generate a thumbnail, instead safe the original
1146 // as thumbnail but with lower quality
1148 if($width >= $cur_width) {
1149 $result = imagejpeg($src_img, $thumb_image, 75);
1150 imagedestroy($src_img);
1154 // If the image will be rotate because EXIF orientation said so
1155 // 'virtually rotate' the image for further calculations
1156 if($rotate == 90 || $rotate == 270) {
1158 $cur_width = $cur_height;
1162 /* calculates aspect ratio */
1163 $aspect_ratio = $cur_height / $cur_width;
1166 if($aspect_ratio < 1) {
1168 $new_h = abs($new_w * $aspect_ratio);
1170 /* 'virtually' rotate the image and calculate it's ratio */
1171 $tmp_w = $cur_height;
1172 $tmp_h = $cur_width;
1173 /* now get the ratio from the 'rotated' image */
1174 $tmp_ratio = $tmp_h/$tmp_w;
1175 /* now calculate the new dimensions */
1177 $tmp_h = abs($tmp_w * $tmp_ratio);
1179 // now that we know, how high they photo should be, if it
1180 // gets rotated, use this high to scale the image
1182 $new_w = abs($new_h / $aspect_ratio);
1184 // If the image will be rotate because EXIF orientation said so
1185 // now 'virtually rotate' back the image for the image manipulation
1186 if($rotate == 90 || $rotate == 270) {
1193 /* creates new image of that size */
1194 $dst_img = imagecreatetruecolor($new_w, $new_h);
1196 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1198 /* copies resized portion of original image into new image */
1199 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1201 /* needs the image to be flipped horizontal? */
1205 for($x = 0; $x < $new_w; $x++) {
1206 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1211 $this->_debug("(ROTATE)");
1212 $dst_img = $this->rotateImage($dst_img, $rotate);
1215 /* write down new generated file */
1216 $result = imagejpeg($dst_img, $thumb_image, 75);
1218 /* free your mind */
1219 imagedestroy($dst_img);
1220 imagedestroy($src_img);
1222 if($result === false) {
1223 print "Can't write thumbnail ". $thumb_image ."\n";
1229 } // create_thumbnail()
1232 * return all exif meta data from the file
1234 public function get_meta_informations($file)
1236 return exif_read_data($file);
1238 } // get_meta_informations()
1241 * create phpfspot own sqlite database
1243 * this function creates phpfspots own sqlite database
1244 * if it does not exist yet. this own is used to store
1245 * some necessary informations (md5 sum's, ...).
1247 public function check_config_table()
1249 // if the config table doesn't exist yet, create it
1250 if(!$this->cfg_db->db_check_table_exists("images")) {
1251 $this->cfg_db->db_exec("
1252 CREATE TABLE images (
1253 img_idx int primary key,
1259 } // check_config_table
1262 * Generates a thumbnail from photo idx
1264 * This function will generate JPEG thumbnails from provided F-Spot photo
1267 * 1. Check if all thumbnail generations (width) are already in place and
1269 * 2. Check if the md5sum of the original file has changed
1270 * 3. Generate the thumbnails if needed
1272 public function gen_thumb($idx = 0, $force = 0)
1276 $resolutions = Array(
1277 $this->cfg->thumb_width,
1278 $this->cfg->photo_width,
1279 $this->cfg->mini_width,
1282 /* get details from F-Spot's database */
1283 $details = $this->get_photo_details($idx);
1285 /* calculate file MD5 sum */
1286 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1288 if(!file_exists($full_path)) {
1289 $this->_error("File ". $full_path ." does not exist\n");
1293 if(!is_readable($full_path)) {
1294 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1298 $file_md5 = md5_file($full_path);
1300 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1304 foreach($resolutions as $resolution) {
1306 $thumb_sub_path = substr($file_md5, 0, 2);
1307 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1309 if(!file_exists(dirname($thumb_path))) {
1310 mkdir(dirname($thumb_path), 0755);
1313 /* if the thumbnail file doesn't exist, create it */
1314 if(!file_exists($thumb_path)) {
1316 $this->_debug(" ". $resolution ."px");
1317 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1322 /* if the file hasn't changed there is no need to regen the thumb */
1323 elseif($file_md5 != $this->getMD5($idx) || $force) {
1325 $this->_debug(" ". $resolution ."px");
1326 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1334 $this->_debug(" already exist");
1337 /* set the new/changed MD5 sum for the current photo */
1339 $this->setMD5($idx, $file_md5);
1342 $this->_debug("\n");
1347 * returns stored md5 sum for a specific photo
1349 * this function queries the phpfspot database for a
1350 * stored MD5 checksum of the specified photo
1352 public function getMD5($idx)
1354 $result = $this->cfg_db->db_query("
1357 WHERE img_idx='". $idx ."'
1363 $img = $this->cfg_db->db_fetch_object($result);
1364 return $img['img_md5'];
1369 * set MD5 sum for the specific photo
1371 private function setMD5($idx, $md5)
1373 $result = $this->cfg_db->db_exec("
1374 REPLACE INTO images (img_idx, img_md5)
1375 VALUES ('". $idx ."', '". $md5 ."')
1381 * store current tag condition
1383 * this function stores the current tag condition
1384 * (AND or OR) in the users session variables
1386 public function setTagCondition($mode)
1388 $_SESSION['tag_condition'] = $mode;
1390 } // setTagCondition()
1393 * invoke tag & date search
1395 * this function will return all matching tags and store
1396 * them in the session variable selected_tags. furthermore
1397 * it also handles the date search.
1398 * getPhotoSelection() will then only return the matching
1401 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1405 $_SESSION['searchfor'] = $searchfor;
1406 $_SESSION['sort_order'] = $sort_order;
1408 $_SESSION['from_date'] = strtotime($from);
1410 unset($_SESSION['from_date']);
1412 $_SESSION['to_date'] = strtotime($to);
1414 unset($_SESSION['to_date']);
1416 if($searchfor != "") {
1417 /* new search, reset the current selected tags */
1418 $_SESSION['selected_tags'] = Array();
1419 foreach($this->avail_tags as $tag) {
1420 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1421 array_push($_SESSION['selected_tags'], $tag);
1430 * this function rotates the image according the
1433 private function rotateImage($img, $degrees)
1435 if(function_exists("imagerotate")) {
1436 $img = imagerotate($img, $degrees, 0);
1438 function imagerotate($src_img, $angle)
1440 $src_x = imagesx($src_img);
1441 $src_y = imagesy($src_img);
1442 if ($angle == 180) {
1446 elseif ($src_x <= $src_y) {
1450 elseif ($src_x >= $src_y) {
1455 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1456 imagealphablending($rotate, false);
1461 for ($y = 0; $y < ($src_y); $y++) {
1462 for ($x = 0; $x < ($src_x); $x++) {
1463 $color = imagecolorat($src_img, $x, $y);
1464 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1470 for ($y = 0; $y < ($src_y); $y++) {
1471 for ($x = 0; $x < ($src_x); $x++) {
1472 $color = imagecolorat($src_img, $x, $y);
1473 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1479 for ($y = 0; $y < ($src_y); $y++) {
1480 for ($x = 0; $x < ($src_x); $x++) {
1481 $color = imagecolorat($src_img, $x, $y);
1482 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1496 $img = imagerotate($img, $degrees);
1505 * return all assigned tags for the specified photo
1507 private function get_photo_tags($idx)
1509 $result = $this->db->db_query("
1512 INNER JOIN photo_tags pt
1514 WHERE pt.photo_id='". $idx ."'
1519 while($row = $this->db->db_fetch_object($result))
1520 $tags[$row['id']] = $row['name'];
1524 } // get_photo_tags()
1527 * create on-the-fly images with text within
1529 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1531 if (strlen($color) != 6)
1534 $int = hexdec($color);
1535 $h = imagefontheight($font);
1536 $fw = imagefontwidth($font);
1537 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1538 $lines = count($txt);
1539 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1540 $bg = imagecolorallocate($im, 255, 255, 255);
1541 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1544 foreach ($txt as $text) {
1545 $x = (($w - ($fw * strlen($text))) / 2);
1546 imagestring($im, $font, $x, $y, $text, $color);
1547 $y += ($h + $space);
1550 Header("Content-type: image/png");
1553 } // showTextImage()
1556 * check if all requirements are met
1558 private function checkRequirements()
1560 if(!function_exists("imagecreatefromjpeg")) {
1561 print "PHP GD library extension is missing<br />\n";
1565 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1566 print "PHP SQLite3 library extension is missing<br />\n";
1570 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1571 ini_set('track_errors', 1);
1572 @include_once 'HTML/AJAX/Server.php';
1573 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1574 print "PEAR HTML_AJAX package is missing<br />\n";
1577 @include_once 'Calendar/Calendar.php';
1578 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1579 print "PEAR Calendar package is missing<br />\n";
1582 ini_restore('track_errors');
1589 } // checkRequirements()
1591 private function _debug($text)
1593 if($this->fromcmd) {
1600 * check if specified MIME type is supported
1602 public function checkifImageSupported($mime)
1604 if(in_array($mime, Array("image/jpeg")))
1609 } // checkifImageSupported()
1611 public function _error($text)
1613 switch($this->cfg->logging) {
1615 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1622 error_log($text, 3, $his->cfg->log_file);
1629 * output calendard input fields
1631 private function get_calendar($mode)
1633 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1634 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1635 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1637 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1638 if(!isset($_SESSION[$mode .'_date']))
1639 $output.= " disabled=\"disabled\"";
1641 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1642 if(!isset($_SESSION[$mode .'_date']))
1643 $output.= " disabled=\"disabled\"";
1645 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1646 if(!isset($_SESSION[$mode .'_date']))
1647 $output.= " disabled=\"disabled\"";
1655 * output calendar matrix
1657 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1659 if (!isset($year)) $year = date('Y');
1660 if (!isset($month)) $month = date('m');
1661 if (!isset($day)) $day = date('d');
1666 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1667 require_once CALENDAR_ROOT.'Day.php';
1670 $month = new Calendar_Month_Weekdays($year,$month);
1673 $prevStamp = $month->prevMonth(true);
1674 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1675 $nextStamp = $month->nextMonth(true);
1676 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1678 $selectedDays = array (
1679 new Calendar_Day($year,$month,$day),
1680 new Calendar_Day($year,12,25),
1683 // Build the days in the month
1684 $month->build($selectedDays);
1686 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1687 $this->tmpl->assign('prev_month', $prev);
1688 $this->tmpl->assign('next_month', $next);
1690 while ( $day = $month->fetch() ) {
1692 if(!isset($matrix[$rows]))
1693 $matrix[$rows] = Array();
1697 $dayStamp = $day->thisDay(true);
1698 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1700 // isFirst() to find start of week
1701 if ( $day->isFirst() )
1704 if ( $day->isSelected() ) {
1705 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1706 } else if ( $day->isEmpty() ) {
1707 $string.= "<td> </td>\n";
1709 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1712 // isLast() to find end of week
1713 if ( $day->isLast() )
1714 $string.= "</tr>\n";
1716 $matrix[$rows][$cols] = $string;
1726 $this->tmpl->assign('matrix', $matrix);
1727 $this->tmpl->assign('rows', $rows);
1728 $this->tmpl->show("calendar.tpl");
1730 } // get_calendar_matrix()
1733 * output export page
1735 public function getExport($mode)
1737 $pictures = $this->getPhotoSelection();
1738 $current_tags = $this->getCurrentTags();
1740 foreach($pictures as $picture) {
1742 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1743 if($current_tags != "") {
1744 $orig_url.= "&tags=". $current_tags;
1746 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1747 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1750 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1755 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1756 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1760 // "[%pictureurl% %thumbnailurl%]"
1761 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1764 case 'MoinMoinList':
1765 // " * [%pictureurl% %thumbnailurl%]"
1766 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1777 public function getRSSFeed()
1779 Header("Content-type: text/xml; charset=utf-8");
1780 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1783 xmlns:media="http://search.yahoo.com/mrss/"
1784 xmlns:dc="http://purl.org/dc/elements/1.1/"
1787 <title>phpfspot</title>
1788 <description>phpfspot RSS feed</description>
1789 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1790 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1791 <generator>phpfspot</generator>
1794 $pictures = $this->getPhotoSelection();
1795 $current_tags = $this->getCurrentTags();
1797 foreach($pictures as $picture) {
1799 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1800 if($current_tags != "") {
1801 $orig_url.= "&tags=". $current_tags;
1803 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1804 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1807 $details = $this->get_photo_details($picture);
1809 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1810 $thumb_html = htmlspecialchars("
1811 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1813 ". $details['description']);
1815 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1816 $meta = $this->get_meta_informations($orig_path);
1817 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1821 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1822 <link><?php print htmlspecialchars($orig_url); ?></link>
1823 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1824 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1826 <?php print $thumb_html; ?>
1828 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1843 * return all selected tags as one string
1845 private function getCurrentTags()
1848 if($_SESSION['selected_tags'] != "") {
1849 foreach($_SESSION['selected_tags'] as $tag)
1850 $current_tags.= $tag .",";
1851 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1853 return $current_tags;
1855 } // getCurrentTags()
1858 * return the current photo
1860 public function getCurrentPhoto()
1862 if(isset($_SESSION['current_photo'])) {
1863 print $_SESSION['current_photo'];
1865 } // getCurrentPhoto()
1868 * tells the client browser what to do
1870 * this function is getting called via AJAX by the
1871 * client browsers. it will tell them what they have
1872 * to do next. This is necessary for directly jumping
1873 * into photo index or single photo view when the are
1874 * requested with specific URLs
1876 public function whatToDo()
1878 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1879 return "show_photo";
1881 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1882 return "showpi_tags";
1884 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1888 return "nothing special";
1893 * return the current process-user
1895 private function getuid()
1897 if($uid = posix_getuid()) {
1898 if($user = posix_getpwuid($uid)) {
1899 return $user['name'];
1908 * returns a select-dropdown box to select photo index sort parameters
1910 private function get_sort_field()
1912 $output = "<select name=\"sort_order\">";
1913 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1914 $output.= "<option value=\"". $sort_order ."\"";
1915 if($sort_order == $_SESSION['sort_order']) {
1916 $output.= " selected=\"selected\"";
1918 $output.= ">". $sort_order ."</option>";
1920 $output.= "</select>";
1923 } // get_sort_field()
1926 * returns the currently selected sort order
1928 private function get_sort_order()
1930 switch($_SESSION['sort_order']) {
1932 return " ORDER BY p.time ASC";
1935 return " ORDER BY p.time DESC";
1938 return " ORDER BY p.name ASC";
1941 return " ORDER BY p.name DESC";
1945 } // get_sort_order()
1948 * return the next to be shown slide show image
1950 * this function returns the URL of the next image
1951 * in the slideshow sequence.
1953 public function getNextSlideShowImage()
1955 $all_photos = $this->getPhotoSelection();
1957 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1958 $_SESSION['slideshow_img'] = 0;
1960 $_SESSION['slideshow_img']++;
1962 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1964 } // getNextSlideShowImage()
1967 * return the previous to be shown slide show image
1969 * this function returns the URL of the previous image
1970 * in the slideshow sequence.
1972 public function getPrevSlideShowImage()
1974 $all_photos = $this->getPhotoSelection();
1976 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1977 $_SESSION['slideshow_img'] = 0;
1979 $_SESSION['slideshow_img']--;
1981 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1983 } // getPrevSlideShowImage()
1985 public function resetSlideShow()
1987 if(isset($_SESSION['slideshow_img']))
1988 unset($_SESSION['slideshow_img']);
1989 } // resetSlideShow()
1994 * this function will get all photos from the fspot
1995 * database and randomly return ONE entry
1997 * saddly there is yet no sqlite3 function which returns
1998 * the bulk result in array, so we have to fill up our
2001 public function get_random_photo()
2005 $result = $this->db->db_query("
2010 while($row = $this->db->db_fetch_object($result)) {
2011 array_push($all, $row['id']);
2014 return $all[array_rand($all)];
2016 } // get_random_photo()
2019 * validates provided date
2021 * this function validates if the provided date
2022 * contains a valid date and will return true
2025 public function isValidDate($date_str)
2027 $timestamp = strtotime($date_str);
2029 if(is_numeric($timestamp))
2037 * timestamp to string conversion
2039 private function ts2str($timestamp)
2041 return strftime("%Y-%m-%d", $timestamp);
2044 private function extractTags($tags_str)
2046 $not_validated = split(',', $_GET['tags']);
2047 $validated = array();
2049 foreach($not_validated as $tag) {
2050 if(is_numeric($tag))
2051 array_push($validated, $tag);
2059 * returns the full path to a thumbnail
2061 public function get_thumb_path($width, $photo)
2063 $md5 = $this->getMD5($photo);
2064 $sub_path = substr($md5, 0, 2);
2065 return $this->cfg->thumb_path
2073 } // get_thumb_path()
2076 * returns server's virtual host name
2078 private function get_server_name()
2080 return $_SERVER['SERVER_NAME'];
2081 } // get_server_name()
2084 * returns type of webprotocol which is
2087 private function get_web_protocol()
2089 if(!isset($_SERVER['HTTPS']))
2093 } // get_web_protocol()
2096 * return url to this phpfspot installation
2098 private function get_phpfspot_url()
2100 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2101 } // get_phpfspot_url()
2104 * check file exists and is readable
2106 * returns true, if everything is ok, otherwise false
2107 * if $silent is not set, this function will output and
2110 private function check_readable($file, $silent = null)
2112 if(!file_exists($file)) {
2114 print "File \"". $file ."\" does not exist.\n";
2118 if(!is_readable($file)) {
2120 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2126 } // check_readable()
2129 * check if all needed indices are present
2131 * this function checks, if some needed indices are already
2132 * present, or if not, create them on the fly. they are
2133 * necessary to speed up some queries like that one look for
2134 * all tags, when show_tags is specified in the configuration.
2136 private function checkDbIndices()
2138 $result = $this->db->db_exec("
2139 CREATE INDEX IF NOT EXISTS
2146 } // checkDbIndices()
2149 * retrive F-Spot database version
2151 * this function will return the F-Spot database version number
2152 * It is stored within the sqlite3 database in the table meta
2154 public function getFspotDBVersion()
2156 if($result = $this->db->db_fetchSingleRow("
2157 SELECT data as version
2160 name LIKE 'F-Spot Database Version'
2162 return $result['version'];
2166 } // getFspotDBVersion()
2169 * parse the provided URI and will returned the
2172 public function parse_uri($uri, $mode)
2174 if(($components = parse_url($uri)) !== false) {
2178 return basename($components['path']);
2181 return dirname($components['path']);
2184 return $components['path'];