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 $_SESSION['start_action'] = $_GET['mode'];
129 switch($_GET['mode']) {
131 if(isset($_GET['tags'])) {
132 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
134 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
135 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
137 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
138 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
142 if(isset($_GET['tags'])) {
143 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
144 $_SESSION['start_action'] = 'showp';
146 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
147 $_SESSION['current_photo'] = $_GET['id'];
148 $_SESSION['start_action'] = 'showp';
150 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
151 $_SESSION['from_date'] = strtotime($_GET['from_date']);
153 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
154 $_SESSION['to_date'] = strtotime($_GET['to_date']);
158 $this->tmpl->show("export.tpl");
162 $this->tmpl->show("slideshow.tpl");
166 if(isset($_GET['tags'])) {
167 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
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");
180 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
181 $this->tmpl->assign('date_search_enabled', true);
183 $this->tmpl->assign('from_date', $this->get_calendar('from'));
184 $this->tmpl->assign('to_date', $this->get_calendar('to'));
185 $this->tmpl->assign('sort_field', $this->get_sort_field());
186 $this->tmpl->assign('content_page', 'welcome.tpl');
187 $this->tmpl->show("index.tpl");
192 * get_tags - grab all tags of f-spot's database
194 * this function will get all available tags from
195 * the f-spot database and store them within two
196 * arrays within this class for later usage. in
197 * fact, if the user requests (hide_tags) it will
198 * opt-out some of them.
200 * this function is getting called once by show()
202 private function get_tags()
204 $this->avail_tags = Array();
207 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
210 DISTINCT t1.id as id, t1.name as name
213 INNER JOIN photo_tags
214 pt2 ON pt1.photo_id=pt2.photo_id
220 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
222 t1.sort_priority ASC";
224 $result = $this->db->db_query($query_str);
228 $result = $this->db->db_query("
231 ORDER BY sort_priority ASC
235 while($row = $this->db->db_fetch_object($result)) {
237 $tag_id = $row['id'];
238 $tag_name = $row['name'];
240 /* if the user has specified to ignore this tag in phpfspot's
241 configuration, ignore it here so it does not get added to
244 if(in_array($row['name'], $this->cfg->hide_tags))
247 /* if you include the following if-clause and the user has specified
248 to only show certain tags which are specified in phpfspot's
249 configuration, ignore all others so they will not be added to the
251 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
252 !in_array($row['name'], $this->cfg->show_tags))
256 $this->tags[$tag_id] = $tag_name;
257 $this->avail_tags[$count] = $tag_id;
265 * extract all photo details
267 * retrieve all available details from f-spot's
268 * database and return them as object
270 public function get_photo_details($idx)
272 if($this->dbver < 9) {
274 SELECT p.id, p.name, p.time, p.directory_path, p.description
280 SELECT p.id, p.uri, p.time, p.description
285 /* if show_tags is set, only return details for photos which
286 are specified to be shown
288 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
290 INNER JOIN photo_tags pt
294 WHERE p.id='". $idx ."'
295 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
299 WHERE p.id='". $idx ."'
303 if($result = $this->db->db_query($query_str)) {
305 $row = $this->db->db_fetch_object($result);
307 if($this->dbver < 9) {
308 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
317 } // get_photo_details
320 * returns aligned photo names
322 * this function returns aligned (length) names for
323 * an specific photo. If the length of the name exceeds
324 * $limit the name will be shrinked (...)
326 public function getPhotoName($idx, $limit = 0)
328 if($details = $this->get_photo_details($idx)) {
329 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
330 $name = $this->shrink_text($long_name, $limit);
340 * shrink text according provided limit
342 * If the length of the name exceeds $limit the
343 * text will be shortend and some content in between
344 * will be replaced with "..."
346 private function shrink_text($text, $limit)
348 if($limit != 0 && strlen($text) > $limit) {
349 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
357 * translate f-spoth photo path
359 * as the full-qualified path recorded in the f-spot database
360 * is usally not the same as on the webserver, this function
361 * will replace the path with that one specified in the cfg
363 public function translate_path($path, $width = 0)
365 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
370 * control HTML ouput for a single photo
372 * this function provides all the necessary information
373 * for the single photo template.
375 public function showPhoto($photo)
377 /* get all photos from the current photo selection */
378 $all_photos = $this->getPhotoSelection();
379 $count = count($all_photos);
381 for($i = 0; $i < $count; $i++) {
383 // $get_next will be set, when the photo which has to
384 // be displayed has been found - this means that the
385 // next available is in fact the NEXT image (for the
387 if(isset($get_next)) {
388 $next_img = $all_photos[$i];
392 /* the next photo is our NEXT photo */
393 if($all_photos[$i] == $photo) {
397 $previous_img = $all_photos[$i];
400 if($photo == $all_photos[$i]) {
405 $details = $this->get_photo_details($photo);
412 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
413 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
415 if(!file_exists($orig_path)) {
416 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
419 if(!is_readable($orig_path)) {
420 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
423 /* If the thumbnail doesn't exist yet, try to create it */
424 if(!file_exists($thumb_path)) {
425 $this->gen_thumb($photo, true);
426 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
429 /* get f-spot database meta information */
430 $meta = $this->get_meta_informations($orig_path);
432 /* If EXIF data are available, use them */
433 if(isset($meta['ExifImageWidth'])) {
434 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
436 $info = getimagesize($orig_path);
437 $meta_res = $info[0] ."x". $info[1];
440 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
441 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
442 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
444 $extern_link = "index.php?mode=showp&id=". $photo;
445 $current_tags = $this->getCurrentTags();
446 if($current_tags != "") {
447 $extern_link.= "&tags=". $current_tags;
449 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
450 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
453 $this->tmpl->assign('extern_link', $extern_link);
455 if(file_exists($thumb_path)) {
457 $info = getimagesize($thumb_path);
459 $this->tmpl->assign('description', $details['description']);
460 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
462 $this->tmpl->assign('width', $info[0]);
463 $this->tmpl->assign('height', $info[1]);
464 $this->tmpl->assign('ExifMadeOn', $meta_date);
465 $this->tmpl->assign('ExifMadeWith', $meta_make);
466 $this->tmpl->assign('ExifOrigResolution', $meta_res);
467 $this->tmpl->assign('ExifFileSize', $meta_size);
469 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
470 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
472 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
473 $this->tmpl->assign('current', $current);
476 $this->_error("Can't open file ". $thumb_path ."\n");
481 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
482 $this->tmpl->assign('prev_img', $previous_img);
486 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
487 $this->tmpl->assign('next_img', $next_img);
489 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
490 $this->tmpl->assign('photo_number', $i);
491 $this->tmpl->assign('photo_count', count($all_photos));
493 $this->tmpl->show("single_photo.tpl");
498 * all available tags and tag cloud
500 * this function outputs all available tags (time ordered)
501 * and in addition output them as tag cloud (tags which have
502 * many photos will appears more then others)
504 public function getAvailableTags()
510 $result = $this->db->db_query("
511 SELECT tag_id as id, count(tag_id) as quantity
521 while($row = $this->db->db_fetch_object($result)) {
522 $tags[$row['id']] = $row['quantity'];
525 // change these font sizes if you will
526 $max_size = 125; // max font size in %
527 $min_size = 75; // min font size in %
529 // get the largest and smallest array values
530 $max_qty = max(array_values($tags));
531 $min_qty = min(array_values($tags));
533 // find the range of values
534 $spread = $max_qty - $min_qty;
535 if (0 == $spread) { // we don't want to divide by zero
539 // determine the font-size increment
540 // this is the increase per tag quantity (times used)
541 $step = ($max_size - $min_size)/($spread);
543 // loop through our tag array
544 foreach ($tags as $key => $value) {
546 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
549 // calculate CSS font-size
550 // find the $value in excess of $min_qty
551 // multiply by the font-size increment ($size)
552 // and add the $min_size set above
553 $size = $min_size + (($value - $min_qty) * $step);
554 // uncomment if you want sizes in whole %:
557 if(isset($this->tags[$key])) {
558 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
563 $output = substr($output, 0, strlen($output)-2);
566 } // getAvailableTags()
569 * output all selected tags
571 * this function output all tags which have been selected
572 * by the user. the selected tags are stored in the
573 * session-variable $_SESSION['selected_tags']
575 public function getSelectedTags()
580 foreach($this->avail_tags as $tag)
582 // return all selected tags
583 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
584 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
588 $output = substr($output, 0, strlen($output)-2);
591 } // getSelectedTags()
594 * add tag to users session variable
596 * this function will add the specified to users current
597 * tag selection. if a date search has been made before
598 * it will be now cleared
600 public function addTag($tag)
602 if(!isset($_SESSION['selected_tags']))
603 $_SESSION['selected_tags'] = Array();
605 if(!in_array($tag, $_SESSION['selected_tags']))
606 array_push($_SESSION['selected_tags'], $tag);
611 * remove tag to users session variable
613 * this function removes the specified tag from
614 * users current tag selection
616 public function delTag($tag)
618 if(isset($_SESSION['selected_tags'])) {
619 $key = array_search($tag, $_SESSION['selected_tags']);
620 unset($_SESSION['selected_tags'][$key]);
621 sort($_SESSION['selected_tags']);
627 * reset tag selection
629 * if there is any tag selection, it will be
632 public function resetTags()
634 if(isset($_SESSION['selected_tags']))
635 unset($_SESSION['selected_tags']);
642 * if a specific photo was requested (external link)
643 * unset the session variable now
645 public function resetPhotoView()
647 if(isset($_SESSION['current_photo']))
648 unset($_SESSION['current_photo']);
650 } // resetPhotoView();
655 * if any tag search has taken place, reset
658 public function resetTagSearch()
660 if(isset($_SESSION['searchfor']))
661 unset($_SESSION['searchfor']);
663 } // resetTagSearch()
668 * if any date search has taken place, reset
671 public function resetDateSearch()
673 if(isset($_SESSION['from_date']))
674 unset($_SESSION['from_date']);
675 if(isset($_SESSION['to_date']))
676 unset($_SESSION['to_date']);
678 } // resetDateSearch();
681 * return all photo according selection
683 * this function returns all photos based on
684 * the tag-selection, tag- or date-search.
685 * the tag-search also has to take care of AND
686 * and OR conjunctions
688 public function getPhotoSelection()
690 $matched_photos = Array();
692 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
693 $from_date = $_SESSION['from_date'];
694 $to_date = $_SESSION['to_date'];
695 $additional_where_cond = "
696 p.time>='". $from_date ."'
698 p.time<='". $to_date ."'
702 if(isset($_SESSION['sort_order'])) {
703 $order_str = $this->get_sort_order();
706 /* return a search result */
707 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
709 SELECT DISTINCT pt1.photo_id
711 INNER JOIN photo_tags pt2
712 ON pt1.photo_id=pt2.photo_id
719 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
721 if(isset($additional_where_cond))
722 $query_str.= "AND ". $additional_where_cond ." ";
724 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
725 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
728 if(isset($order_str))
729 $query_str.= $order_str;
731 $result = $this->db->db_query($query_str);
732 while($row = $this->db->db_fetch_object($result)) {
733 array_push($matched_photos, $row['photo_id']);
735 return $matched_photos;
738 /* return according the selected tags */
739 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
741 foreach($_SESSION['selected_tags'] as $tag)
742 $selected.= $tag .",";
743 $selected = substr($selected, 0, strlen($selected)-1);
745 /* photo has to match at least on of the selected tags */
746 if($_SESSION['tag_condition'] == 'or') {
748 SELECT DISTINCT pt1.photo_id
750 INNER JOIN photo_tags pt2
751 ON pt1.photo_id=pt2.photo_id
756 WHERE pt1.tag_id IN (". $selected .")
758 if(isset($additional_where_cond))
759 $query_str.= "AND ". $additional_where_cond ." ";
761 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
762 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
765 if(isset($order_str))
766 $query_str.= $order_str;
768 /* photo has to match all selected tags */
769 elseif($_SESSION['tag_condition'] == 'and') {
771 if(count($_SESSION['selected_tags']) >= 32) {
772 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
773 print "evaluate your tag selection. Please remove some tags from your selection.\n";
777 /* Join together a table looking like
779 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
781 so the query can quickly return all images matching the
782 selected tags in an AND condition
787 SELECT DISTINCT pt1.photo_id
791 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
798 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
800 INNER JOIN photo_tags pt". ($i+2) ."
801 ON pt1.photo_id=pt". ($i+2) .".photo_id
808 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
809 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
811 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
814 if(isset($additional_where_cond))
815 $query_str.= "AND ". $additional_where_cond;
817 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
818 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
821 if(isset($order_str))
822 $query_str.= $order_str;
826 $result = $this->db->db_query($query_str);
827 while($row = $this->db->db_fetch_object($result)) {
828 array_push($matched_photos, $row['photo_id']);
830 return $matched_photos;
833 /* return all available photos */
835 SELECT DISTINCT photo_id
842 if(isset($additional_where_cond))
843 $query_str.= "WHERE ". $additional_where_cond ." ";
845 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
846 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
849 if(isset($order_str))
850 $query_str.= $order_str;
852 $result = $this->db->db_query($query_str);
853 while($row = $this->db->db_fetch_object($result)) {
854 array_push($matched_photos, $row['photo_id']);
856 return $matched_photos;
858 } // getPhotoSelection()
861 * control HTML ouput for photo index
863 * this function provides all the necessary information
864 * for the photo index template.
866 public function showPhotoIndex()
868 $photos = $this->getPhotoSelection();
870 $count = count($photos);
872 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
873 $anchor = $_SESSION['begin_with'];
875 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
881 elseif($this->cfg->rows_per_page > 0) {
883 if(!$_SESSION['begin_with'] || $_SESSION['begin_with'] == 0)
887 $begin_with = $_SESSION['begin_with'];
889 // verify $begin_with - perhaps the thumbs-per-rows or
890 // rows-per-page variables have changed or the jump back
891 // from a photo wasn't exact - so calculate the real new
893 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
894 for($i = 0; $i <= $count; $i+=$multiplicator) {
895 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
902 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
908 $images[$rows] = Array();
909 $img_height[$rows] = Array();
910 $img_width[$rows] = Array();
911 $img_id[$rows] = Array();
912 $img_name[$rows] = Array();
913 $img_title = Array();
915 for($i = $begin_with; $i < $end_with; $i++) {
917 $images[$rows][$cols] = $photos[$i];
918 $img_id[$rows][$cols] = $i;
919 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
920 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
922 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
924 if(file_exists($thumb_path)) {
925 $info = getimagesize($thumb_path);
926 $img_width[$rows][$cols] = $info[0];
927 $img_height[$rows][$cols] = $info[1];
930 if($cols == $this->cfg->thumbs_per_row-1) {
933 $images[$rows] = Array();
934 $img_width[$rows] = Array();
935 $img_height[$rows] = Array();
942 // +1 for for smarty's selection iteration
945 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
946 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
948 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
949 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
950 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
953 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
954 $this->tmpl->assign('tag_result', 1);
957 /* do we have to display the page selector ? */
958 if($this->cfg->rows_per_page != 0) {
960 /* calculate the page switchers */
961 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
962 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
965 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
966 if($end_with < $count)
967 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
969 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
970 $last_page = ceil($count / $photo_per_page);
972 /* get the current selected page */
973 if($begin_with == 0) {
977 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
984 for($i = 1; $i <= $last_page; $i++) {
986 if($current_page == $i)
987 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
988 elseif($current_page-1 == $i || $current_page+1 == $i)
989 $style = "style=\"font-size: 105%;\"";
990 elseif(($current_page-5 >= $i) && ($i != 1) ||
991 ($current_page+5 <= $i) && ($i != $last_page))
992 $style = "style=\"font-size: 75%;\"";
996 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
999 $select.= ">". $i ."</a> ";
1001 // until 9 pages we show the selector from 1-9
1002 if($last_page <= 9) {
1003 $page_select.= $select;
1006 if($i == 1 /* first page */ ||
1007 $i == $last_page /* last page */ ||
1008 $i == $current_page /* current page */ ||
1009 $i == ceil($last_page * 0.25) /* first quater */ ||
1010 $i == ceil($last_page * 0.5) /* half */ ||
1011 $i == ceil($last_page * 0.75) /* third quater */ ||
1012 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1013 (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 */ ||
1014 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1015 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1017 $page_select.= $select;
1025 $page_select.= "......... ";
1030 /* only show the page selector if we have more then one page */
1032 $this->tmpl->assign('page_selector', $page_select);
1036 $current_tags = $this->getCurrentTags();
1037 $extern_link = "index.php?mode=showpi";
1038 $rss_link = "index.php?mode=rss";
1039 if($current_tags != "") {
1040 $extern_link.= "&tags=". $current_tags;
1041 $rss_link.= "&tags=". $current_tags;
1043 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1044 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1045 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1048 $export_link = "index.php?mode=export";
1049 $slideshow_link = "index.php?mode=slideshow";
1051 $this->tmpl->assign('extern_link', $extern_link);
1052 $this->tmpl->assign('slideshow_link', $slideshow_link);
1053 $this->tmpl->assign('export_link', $export_link);
1054 $this->tmpl->assign('rss_link', $rss_link);
1055 $this->tmpl->assign('count', $count);
1056 $this->tmpl->assign('width', $this->cfg->thumb_width);
1057 $this->tmpl->assign('images', $images);
1058 $this->tmpl->assign('img_width', $img_width);
1059 $this->tmpl->assign('img_height', $img_height);
1060 $this->tmpl->assign('img_id', $img_id);
1061 $this->tmpl->assign('img_name', $img_name);
1062 $this->tmpl->assign('img_title', $img_title);
1063 $this->tmpl->assign('rows', $rows);
1064 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1066 $this->tmpl->show("photo_index.tpl");
1069 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1071 } // showPhotoIndex()
1074 * show credit template
1076 public function showCredits()
1078 $this->tmpl->assign('version', $this->cfg->version);
1079 $this->tmpl->assign('product', $this->cfg->product);
1080 $this->tmpl->assign('db_version', $this->dbver);
1081 $this->tmpl->show("credits.tpl");
1086 * create_thumbnails for the requested width
1088 * this function creates image thumbnails of $orig_image
1089 * stored as $thumb_image. It will check if the image is
1090 * in a supported format, if necessary rotate the image
1091 * (based on EXIF orientation meta headers) and re-sizing.
1093 public function create_thumbnail($orig_image, $thumb_image, $width)
1095 if(!file_exists($orig_image)) {
1099 $details = getimagesize($orig_image);
1101 /* check if original photo is a support image type */
1102 if(!$this->checkifImageSupported($details['mime']))
1105 $meta = $this->get_meta_informations($orig_image);
1110 switch($meta['Orientation']) {
1112 case 1: /* top, left */
1113 $rotate = 0; $flip = false; break;
1114 case 2: /* top, right */
1115 $rotate = 0; $flip = true; break;
1116 case 3: /* bottom, left */
1117 $rotate = 180; $flip = false; break;
1118 case 4: /* bottom, right */
1119 $rotate = 180; $flip = true; break;
1120 case 5: /* left side, top */
1121 $rotate = 90; $flip = true; break;
1122 case 6: /* right side, top */
1123 $rotate = 90; $flip = false; break;
1124 case 7: /* left side, bottom */
1125 $rotate = 270; $flip = true; break;
1126 case 8: /* right side, bottom */
1127 $rotate = 270; $flip = false; break;
1130 $src_img = @imagecreatefromjpeg($orig_image);
1133 print "Can't load image from ". $orig_image ."\n";
1137 /* grabs the height and width */
1138 $cur_width = imagesx($src_img);
1139 $cur_height = imagesy($src_img);
1141 // If requested width is more then the actual image width,
1142 // do not generate a thumbnail, instead safe the original
1143 // as thumbnail but with lower quality
1145 if($width >= $cur_width) {
1146 $result = imagejpeg($src_img, $thumb_image, 75);
1147 imagedestroy($src_img);
1151 // If the image will be rotate because EXIF orientation said so
1152 // 'virtually rotate' the image for further calculations
1153 if($rotate == 90 || $rotate == 270) {
1155 $cur_width = $cur_height;
1159 /* calculates aspect ratio */
1160 $aspect_ratio = $cur_height / $cur_width;
1163 if($aspect_ratio < 1) {
1165 $new_h = abs($new_w * $aspect_ratio);
1167 /* 'virtually' rotate the image and calculate it's ratio */
1168 $tmp_w = $cur_height;
1169 $tmp_h = $cur_width;
1170 /* now get the ratio from the 'rotated' image */
1171 $tmp_ratio = $tmp_h/$tmp_w;
1172 /* now calculate the new dimensions */
1174 $tmp_h = abs($tmp_w * $tmp_ratio);
1176 // now that we know, how high they photo should be, if it
1177 // gets rotated, use this high to scale the image
1179 $new_w = abs($new_h / $aspect_ratio);
1181 // If the image will be rotate because EXIF orientation said so
1182 // now 'virtually rotate' back the image for the image manipulation
1183 if($rotate == 90 || $rotate == 270) {
1190 /* creates new image of that size */
1191 $dst_img = imagecreatetruecolor($new_w, $new_h);
1193 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1195 /* copies resized portion of original image into new image */
1196 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1198 /* needs the image to be flipped horizontal? */
1202 for($x = 0; $x < $new_w; $x++) {
1203 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1208 $this->_debug("(ROTATE)");
1209 $dst_img = $this->rotateImage($dst_img, $rotate);
1212 /* write down new generated file */
1213 $result = imagejpeg($dst_img, $thumb_image, 75);
1215 /* free your mind */
1216 imagedestroy($dst_img);
1217 imagedestroy($src_img);
1219 if($result === false) {
1220 print "Can't write thumbnail ". $thumb_image ."\n";
1226 } // create_thumbnail()
1229 * return all exif meta data from the file
1231 public function get_meta_informations($file)
1233 return exif_read_data($file);
1235 } // get_meta_informations()
1238 * create phpfspot own sqlite database
1240 * this function creates phpfspots own sqlite database
1241 * if it does not exist yet. this own is used to store
1242 * some necessary informations (md5 sum's, ...).
1244 public function check_config_table()
1246 // if the config table doesn't exist yet, create it
1247 if(!$this->cfg_db->db_check_table_exists("images")) {
1248 $this->cfg_db->db_exec("
1249 CREATE TABLE images (
1250 img_idx int primary key,
1256 } // check_config_table
1259 * Generates a thumbnail from photo idx
1261 * This function will generate JPEG thumbnails from provided F-Spot photo
1264 * 1. Check if all thumbnail generations (width) are already in place and
1266 * 2. Check if the md5sum of the original file has changed
1267 * 3. Generate the thumbnails if needed
1269 public function gen_thumb($idx = 0, $force = 0)
1273 $resolutions = Array(
1274 $this->cfg->thumb_width,
1275 $this->cfg->photo_width,
1276 $this->cfg->mini_width,
1279 /* get details from F-Spot's database */
1280 $details = $this->get_photo_details($idx);
1282 /* calculate file MD5 sum */
1283 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1285 if(!file_exists($full_path)) {
1286 $this->_error("File ". $full_path ." does not exist\n");
1290 if(!is_readable($full_path)) {
1291 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1295 $file_md5 = md5_file($full_path);
1297 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1301 foreach($resolutions as $resolution) {
1303 $thumb_sub_path = substr($file_md5, 0, 2);
1304 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1306 if(!file_exists(dirname($thumb_path))) {
1307 mkdir(dirname($thumb_path), 0755);
1310 /* if the thumbnail file doesn't exist, create it */
1311 if(!file_exists($thumb_path)) {
1313 $this->_debug(" ". $resolution ."px");
1314 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1319 /* if the file hasn't changed there is no need to regen the thumb */
1320 elseif($file_md5 != $this->getMD5($idx) || $force) {
1322 $this->_debug(" ". $resolution ."px");
1323 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1331 $this->_debug(" already exist");
1334 /* set the new/changed MD5 sum for the current photo */
1336 $this->setMD5($idx, $file_md5);
1339 $this->_debug("\n");
1344 * returns stored md5 sum for a specific photo
1346 * this function queries the phpfspot database for a
1347 * stored MD5 checksum of the specified photo
1349 public function getMD5($idx)
1351 $result = $this->cfg_db->db_query("
1354 WHERE img_idx='". $idx ."'
1360 $img = $this->cfg_db->db_fetch_object($result);
1361 return $img['img_md5'];
1366 * set MD5 sum for the specific photo
1368 private function setMD5($idx, $md5)
1370 $result = $this->cfg_db->db_exec("
1371 REPLACE INTO images (img_idx, img_md5)
1372 VALUES ('". $idx ."', '". $md5 ."')
1378 * store current tag condition
1380 * this function stores the current tag condition
1381 * (AND or OR) in the users session variables
1383 public function setTagCondition($mode)
1385 $_SESSION['tag_condition'] = $mode;
1387 } // setTagCondition()
1390 * invoke tag & date search
1392 * this function will return all matching tags and store
1393 * them in the session variable selected_tags. furthermore
1394 * it also handles the date search.
1395 * getPhotoSelection() will then only return the matching
1398 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1402 $_SESSION['searchfor'] = $searchfor;
1403 $_SESSION['sort_order'] = $sort_order;
1405 $_SESSION['from_date'] = strtotime($from);
1407 unset($_SESSION['from_date']);
1409 $_SESSION['to_date'] = strtotime($to);
1411 unset($_SESSION['to_date']);
1413 if($searchfor != "") {
1414 /* new search, reset the current selected tags */
1415 $_SESSION['selected_tags'] = Array();
1416 foreach($this->avail_tags as $tag) {
1417 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1418 array_push($_SESSION['selected_tags'], $tag);
1427 * this function rotates the image according the
1430 private function rotateImage($img, $degrees)
1432 if(function_exists("imagerotate")) {
1433 $img = imagerotate($img, $degrees, 0);
1435 function imagerotate($src_img, $angle)
1437 $src_x = imagesx($src_img);
1438 $src_y = imagesy($src_img);
1439 if ($angle == 180) {
1443 elseif ($src_x <= $src_y) {
1447 elseif ($src_x >= $src_y) {
1452 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1453 imagealphablending($rotate, false);
1458 for ($y = 0; $y < ($src_y); $y++) {
1459 for ($x = 0; $x < ($src_x); $x++) {
1460 $color = imagecolorat($src_img, $x, $y);
1461 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1467 for ($y = 0; $y < ($src_y); $y++) {
1468 for ($x = 0; $x < ($src_x); $x++) {
1469 $color = imagecolorat($src_img, $x, $y);
1470 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1476 for ($y = 0; $y < ($src_y); $y++) {
1477 for ($x = 0; $x < ($src_x); $x++) {
1478 $color = imagecolorat($src_img, $x, $y);
1479 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1493 $img = imagerotate($img, $degrees);
1502 * return all assigned tags for the specified photo
1504 private function get_photo_tags($idx)
1506 $result = $this->db->db_query("
1509 INNER JOIN photo_tags pt
1511 WHERE pt.photo_id='". $idx ."'
1516 while($row = $this->db->db_fetch_object($result))
1517 $tags[$row['id']] = $row['name'];
1521 } // get_photo_tags()
1524 * create on-the-fly images with text within
1526 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1528 if (strlen($color) != 6)
1531 $int = hexdec($color);
1532 $h = imagefontheight($font);
1533 $fw = imagefontwidth($font);
1534 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1535 $lines = count($txt);
1536 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1537 $bg = imagecolorallocate($im, 255, 255, 255);
1538 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1541 foreach ($txt as $text) {
1542 $x = (($w - ($fw * strlen($text))) / 2);
1543 imagestring($im, $font, $x, $y, $text, $color);
1544 $y += ($h + $space);
1547 Header("Content-type: image/png");
1550 } // showTextImage()
1553 * check if all requirements are met
1555 private function checkRequirements()
1557 if(!function_exists("imagecreatefromjpeg")) {
1558 print "PHP GD library extension is missing<br />\n";
1562 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1563 print "PHP SQLite3 library extension is missing<br />\n";
1567 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1568 ini_set('track_errors', 1);
1569 @include_once 'HTML/AJAX/Server.php';
1570 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1571 print "PEAR HTML_AJAX package is missing<br />\n";
1574 @include_once 'Calendar/Calendar.php';
1575 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1576 print "PEAR Calendar package is missing<br />\n";
1579 ini_restore('track_errors');
1586 } // checkRequirements()
1588 private function _debug($text)
1590 if($this->fromcmd) {
1597 * check if specified MIME type is supported
1599 public function checkifImageSupported($mime)
1601 if(in_array($mime, Array("image/jpeg")))
1606 } // checkifImageSupported()
1608 public function _error($text)
1610 switch($this->cfg->logging) {
1612 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1619 error_log($text, 3, $his->cfg->log_file);
1626 * output calendard input fields
1628 private function get_calendar($mode)
1630 $year = $_SESSION[$mode .'_date'] ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1631 $month = $_SESSION[$mode .'_date'] ? date("m", $_SESSION[$mode .'_date']) : date("m");
1632 $day = $_SESSION[$mode .'_date'] ? date("d", $_SESSION[$mode .'_date']) : date("d");
1634 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1635 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1637 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1638 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1640 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1641 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1648 * output calendar matrix
1650 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1652 if (!isset($year)) $year = date('Y');
1653 if (!isset($month)) $month = date('m');
1654 if (!isset($day)) $day = date('d');
1659 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1660 require_once CALENDAR_ROOT.'Day.php';
1663 $month = new Calendar_Month_Weekdays($year,$month);
1666 $prevStamp = $month->prevMonth(true);
1667 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1668 $nextStamp = $month->nextMonth(true);
1669 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1671 $selectedDays = array (
1672 new Calendar_Day($year,$month,$day),
1673 new Calendar_Day($year,12,25),
1676 // Build the days in the month
1677 $month->build($selectedDays);
1679 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1680 $this->tmpl->assign('prev_month', $prev);
1681 $this->tmpl->assign('next_month', $next);
1683 while ( $day = $month->fetch() ) {
1685 if(!isset($matrix[$rows]))
1686 $matrix[$rows] = Array();
1690 $dayStamp = $day->thisDay(true);
1691 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1693 // isFirst() to find start of week
1694 if ( $day->isFirst() )
1697 if ( $day->isSelected() ) {
1698 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1699 } else if ( $day->isEmpty() ) {
1700 $string.= "<td> </td>\n";
1702 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1705 // isLast() to find end of week
1706 if ( $day->isLast() )
1707 $string.= "</tr>\n";
1709 $matrix[$rows][$cols] = $string;
1719 $this->tmpl->assign('matrix', $matrix);
1720 $this->tmpl->assign('rows', $rows);
1721 $this->tmpl->show("calendar.tpl");
1723 } // get_calendar_matrix()
1726 * output export page
1728 public function getExport($mode)
1730 $pictures = $this->getPhotoSelection();
1731 $current_tags = $this->getCurrentTags();
1733 foreach($pictures as $picture) {
1735 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1736 if($current_tags != "") {
1737 $orig_url.= "&tags=". $current_tags;
1739 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1740 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1743 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1748 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1749 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1753 // "[%pictureurl% %thumbnailurl%]"
1754 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1757 case 'MoinMoinList':
1758 // " * [%pictureurl% %thumbnailurl%]"
1759 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1770 public function getRSSFeed()
1772 Header("Content-type: text/xml; charset=utf-8");
1773 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1776 xmlns:media="http://search.yahoo.com/mrss/"
1777 xmlns:dc="http://purl.org/dc/elements/1.1/"
1780 <title>phpfspot</title>
1781 <description>phpfspot RSS feed</description>
1782 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1783 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1784 <generator>phpfspot</generator>
1787 $pictures = $this->getPhotoSelection();
1788 $current_tags = $this->getCurrentTags();
1790 foreach($pictures as $picture) {
1792 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1793 if($current_tags != "") {
1794 $orig_url.= "&tags=". $current_tags;
1796 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1797 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1800 $details = $this->get_photo_details($picture);
1802 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1803 $thumb_html = htmlspecialchars("
1804 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1806 ". $details['description']);
1808 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1809 $meta = $this->get_meta_informations($orig_path);
1810 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1814 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1815 <link><?php print htmlspecialchars($orig_url); ?></link>
1816 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1817 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1819 <?php print $thumb_html; ?>
1821 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1836 * return all selected tags as one string
1838 private function getCurrentTags()
1841 if($_SESSION['selected_tags'] != "") {
1842 foreach($_SESSION['selected_tags'] as $tag)
1843 $current_tags.= $tag .",";
1844 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1846 return $current_tags;
1848 } // getCurrentTags()
1851 * return the current photo
1853 public function getCurrentPhoto()
1855 if(isset($_SESSION['current_photo'])) {
1856 print $_SESSION['current_photo'];
1858 } // getCurrentPhoto()
1861 * tells the client browser what to do
1863 * this function is getting called via AJAX by the
1864 * client browsers. it will tell them what they have
1865 * to do next. This is necessary for directly jumping
1866 * into photo index or single photo view when the are
1867 * requested with specific URLs
1869 public function whatToDo()
1871 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1872 return "show_photo";
1874 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1875 return "showpi_tags";
1877 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1881 return "nothing special";
1886 * return the current process-user
1888 private function getuid()
1890 if($uid = posix_getuid()) {
1891 if($user = posix_getpwuid($uid)) {
1892 return $user['name'];
1901 * returns a select-dropdown box to select photo index sort parameters
1903 private function get_sort_field()
1905 $output = "<select name=\"sort_order\">";
1906 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1907 $output.= "<option value=\"". $sort_order ."\"";
1908 if($sort_order == $_SESSION['sort_order']) {
1909 $output.= " selected=\"selected\"";
1911 $output.= ">". $sort_order ."</option>";
1913 $output.= "</select>";
1916 } // get_sort_field()
1919 * returns the currently selected sort order
1921 private function get_sort_order()
1923 switch($_SESSION['sort_order']) {
1925 return " ORDER BY p.time ASC";
1928 return " ORDER BY p.time DESC";
1931 return " ORDER BY p.name ASC";
1934 return " ORDER BY p.name DESC";
1938 } // get_sort_order()
1941 * return the next to be shown slide show image
1943 * this function returns the URL of the next image
1944 * in the slideshow sequence.
1946 public function getNextSlideShowImage()
1948 $all_photos = $this->getPhotoSelection();
1950 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1951 $_SESSION['slideshow_img'] = 0;
1953 $_SESSION['slideshow_img']++;
1955 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1957 } // getNextSlideShowImage()
1960 * return the previous to be shown slide show image
1962 * this function returns the URL of the previous image
1963 * in the slideshow sequence.
1965 public function getPrevSlideShowImage()
1967 $all_photos = $this->getPhotoSelection();
1969 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1970 $_SESSION['slideshow_img'] = 0;
1972 $_SESSION['slideshow_img']--;
1974 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1976 } // getPrevSlideShowImage()
1978 public function resetSlideShow()
1980 if(isset($_SESSION['slideshow_img']))
1981 unset($_SESSION['slideshow_img']);
1982 } // resetSlideShow()
1987 * this function will get all photos from the fspot
1988 * database and randomly return ONE entry
1990 * saddly there is yet no sqlite3 function which returns
1991 * the bulk result in array, so we have to fill up our
1994 public function get_random_photo()
1998 $result = $this->db->db_query("
2003 while($row = $this->db->db_fetch_object($result)) {
2004 array_push($all, $row['id']);
2007 return $all[array_rand($all)];
2009 } // get_random_photo()
2012 * validates provided date
2014 * this function validates if the provided date
2015 * contains a valid date and will return true
2018 public function isValidDate($date_str)
2020 $timestamp = strtotime($date_str);
2022 if(is_numeric($timestamp))
2030 * timestamp to string conversion
2032 private function ts2str($timestamp)
2034 return strftime("%Y-%m-%d", $timestamp);
2037 private function extractTags($tags_str)
2039 $not_validated = split(',', $_GET['tags']);
2040 $validated = array();
2042 foreach($not_validated as $tag) {
2043 if(is_numeric($tag))
2044 array_push($validated, $tag);
2052 * returns the full path to a thumbnail
2054 public function get_thumb_path($width, $photo)
2056 $md5 = $this->getMD5($photo);
2057 $sub_path = substr($md5, 0, 2);
2058 return $this->cfg->thumb_path
2066 } // get_thumb_path()
2069 * returns server's virtual host name
2071 private function get_server_name()
2073 return $_SERVER['SERVER_NAME'];
2074 } // get_server_name()
2077 * returns type of webprotocol which is
2080 private function get_web_protocol()
2082 if(!isset($_SERVER['HTTPS']))
2086 } // get_web_protocol()
2089 * return url to this phpfspot installation
2091 private function get_phpfspot_url()
2093 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2094 } // get_phpfspot_url()
2097 * check file exists and is readable
2099 * returns true, if everything is ok, otherwise false
2100 * if $silent is not set, this function will output and
2103 private function check_readable($file, $silent = null)
2105 if(!file_exists($file)) {
2107 print "File \"". $file ."\" does not exist.\n";
2111 if(!is_readable($file)) {
2113 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2119 } // check_readable()
2122 * check if all needed indices are present
2124 * this function checks, if some needed indices are already
2125 * present, or if not, create them on the fly. they are
2126 * necessary to speed up some queries like that one look for
2127 * all tags, when show_tags is specified in the configuration.
2129 private function checkDbIndices()
2131 $result = $this->db->db_exec("
2132 CREATE INDEX IF NOT EXISTS
2139 } // checkDbIndices()
2142 * retrive F-Spot database version
2144 * this function will return the F-Spot database version number
2145 * It is stored within the sqlite3 database in the table meta
2147 public function getFspotDBVersion()
2149 if($result = $this->db->db_fetchSingleRow("
2150 SELECT data as version
2153 name LIKE 'F-Spot Database Version'
2155 return $result['version'];
2159 } // getFspotDBVersion()
2162 * parse the provided URI and will returned the
2165 public function parse_uri($uri, $mode)
2167 if(($components = parse_url($uri)) !== false) {
2171 return basename($components['path']);
2174 return dirname($components['path']);
2177 return $components['path'];