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 for user ". $this->getuid() ."\n";
70 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
71 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
75 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
76 if(!is_writeable($this->cfg->phpfspot_db)) {
77 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
81 $this->check_config_table();
83 /* include Smarty template engine */
84 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
87 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
88 /* overload Smarty class if our own template handler */
89 require_once "phpfspot_tmpl.php";
90 $this->tmpl = new PHPFSPOT_TMPL($this);
92 /* check if all necessary indices exist */
93 $this->checkDbIndices();
95 /* if session is not yet started, do it now */
96 if(session_id() == "")
99 if(!isset($_SESSION['tag_condition']))
100 $_SESSION['tag_condition'] = 'or';
102 if(!isset($_SESSION['sort_order']))
103 $_SESSION['sort_order'] = 'date_asc';
105 if(!isset($_SESSION['searchfor']))
106 $_SESSION['searchfor'] = '';
108 // if begin_with is still set but rows_per_page is now 0, unset it
109 if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
110 unset($_SESSION['begin_with']);
114 public function __destruct()
120 * show - generate html output
122 * this function can be called after the constructor has
123 * prepared everyhing. it will load the index.tpl smarty
124 * template. if necessary it will registere pre-selects
125 * (photo index, photo, tag search, date search) into
128 public function show()
130 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
131 $this->tmpl->assign('page_title', $this->cfg->page_title);
132 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
133 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
135 if(isset($_GET['mode'])) {
137 $_SESSION['start_action'] = $_GET['mode'];
139 switch($_GET['mode']) {
141 if(isset($_GET['tags'])) {
142 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
144 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
145 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
147 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
148 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
152 if(isset($_GET['tags'])) {
153 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
154 $_SESSION['start_action'] = 'showp';
156 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
157 $_SESSION['current_photo'] = $_GET['id'];
158 $_SESSION['start_action'] = 'showp';
160 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
161 $_SESSION['from_date'] = strtotime($_GET['from_date']);
163 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
164 $_SESSION['to_date'] = strtotime($_GET['to_date']);
168 $this->tmpl->show("export.tpl");
172 $this->tmpl->show("slideshow.tpl");
176 if(isset($_GET['tags'])) {
177 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
179 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
180 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
182 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
183 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
191 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
192 $this->tmpl->assign('date_search_enabled', true);
194 $this->tmpl->assign('from_date', $this->get_calendar('from'));
195 $this->tmpl->assign('to_date', $this->get_calendar('to'));
196 $this->tmpl->assign('sort_field', $this->get_sort_field());
197 $this->tmpl->assign('content_page', 'welcome.tpl');
198 $this->tmpl->show("index.tpl");
203 * get_tags - grab all tags of f-spot's database
205 * this function will get all available tags from
206 * the f-spot database and store them within two
207 * arrays within this class for later usage. in
208 * fact, if the user requests (hide_tags) it will
209 * opt-out some of them.
211 * this function is getting called once by show()
213 private function get_tags()
215 $this->avail_tags = Array();
218 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
221 DISTINCT t1.id as id, t1.name as name
224 INNER JOIN photo_tags
225 pt2 ON pt1.photo_id=pt2.photo_id
231 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
233 t1.sort_priority ASC";
235 $result = $this->db->db_query($query_str);
239 $result = $this->db->db_query("
242 ORDER BY sort_priority ASC
246 while($row = $this->db->db_fetch_object($result)) {
248 $tag_id = $row['id'];
249 $tag_name = $row['name'];
251 /* if the user has specified to ignore this tag in phpfspot's
252 configuration, ignore it here so it does not get added to
255 if(in_array($row['name'], $this->cfg->hide_tags))
258 /* if you include the following if-clause and the user has specified
259 to only show certain tags which are specified in phpfspot's
260 configuration, ignore all others so they will not be added to the
262 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
263 !in_array($row['name'], $this->cfg->show_tags))
267 $this->tags[$tag_id] = $tag_name;
268 $this->avail_tags[$count] = $tag_id;
276 * extract all photo details
278 * retrieve all available details from f-spot's
279 * database and return them as object
281 public function get_photo_details($idx)
283 if($this->dbver < 9) {
285 SELECT p.id, p.name, p.time, p.directory_path, p.description
291 SELECT p.id, p.uri, p.time, p.description
296 /* if show_tags is set, only return details for photos which
297 are specified to be shown
299 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
301 INNER JOIN photo_tags pt
305 WHERE p.id='". $idx ."'
306 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
310 WHERE p.id='". $idx ."'
314 if($result = $this->db->db_query($query_str)) {
316 $row = $this->db->db_fetch_object($result);
318 if($this->dbver < 9) {
319 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
328 } // get_photo_details
331 * returns aligned photo names
333 * this function returns aligned (length) names for
334 * an specific photo. If the length of the name exceeds
335 * $limit the name will be shrinked (...)
337 public function getPhotoName($idx, $limit = 0)
339 if($details = $this->get_photo_details($idx)) {
340 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
341 $name = $this->shrink_text($long_name, $limit);
351 * shrink text according provided limit
353 * If the length of the name exceeds $limit the
354 * text will be shortend and some content in between
355 * will be replaced with "..."
357 private function shrink_text($text, $limit)
359 if($limit != 0 && strlen($text) > $limit) {
360 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
368 * translate f-spoth photo path
370 * as the full-qualified path recorded in the f-spot database
371 * is usally not the same as on the webserver, this function
372 * will replace the path with that one specified in the cfg
374 public function translate_path($path, $width = 0)
376 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
381 * control HTML ouput for a single photo
383 * this function provides all the necessary information
384 * for the single photo template.
386 public function showPhoto($photo)
388 /* get all photos from the current photo selection */
389 $all_photos = $this->getPhotoSelection();
390 $count = count($all_photos);
392 for($i = 0; $i < $count; $i++) {
394 // $get_next will be set, when the photo which has to
395 // be displayed has been found - this means that the
396 // next available is in fact the NEXT image (for the
398 if(isset($get_next)) {
399 $next_img = $all_photos[$i];
403 /* the next photo is our NEXT photo */
404 if($all_photos[$i] == $photo) {
408 $previous_img = $all_photos[$i];
411 if($photo == $all_photos[$i]) {
416 $details = $this->get_photo_details($photo);
423 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
424 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
426 if(!file_exists($orig_path)) {
427 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
431 if(!is_readable($orig_path)) {
432 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
436 /* If the thumbnail doesn't exist yet, try to create it */
437 if(!file_exists($thumb_path)) {
438 $this->gen_thumb($photo, true);
439 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
442 /* get f-spot database meta information */
443 $meta = $this->get_meta_informations($orig_path);
445 /* If EXIF data are available, use them */
446 if(isset($meta['ExifImageWidth'])) {
447 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
449 $info = getimagesize($orig_path);
450 $meta_res = $info[0] ."x". $info[1];
453 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
454 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
455 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
457 $extern_link = "index.php?mode=showp&id=". $photo;
458 $current_tags = $this->getCurrentTags();
459 if($current_tags != "") {
460 $extern_link.= "&tags=". $current_tags;
462 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
463 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
466 $this->tmpl->assign('extern_link', $extern_link);
468 if(file_exists($thumb_path)) {
470 $info = getimagesize($thumb_path);
472 $this->tmpl->assign('description', $details['description']);
473 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
475 $this->tmpl->assign('width', $info[0]);
476 $this->tmpl->assign('height', $info[1]);
477 $this->tmpl->assign('ExifMadeOn', $meta_date);
478 $this->tmpl->assign('ExifMadeWith', $meta_make);
479 $this->tmpl->assign('ExifOrigResolution', $meta_res);
480 $this->tmpl->assign('ExifFileSize', $meta_size);
482 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
483 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
485 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
486 $this->tmpl->assign('current', $current);
489 $this->_error("Can't open file ". $thumb_path ."\n");
494 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
495 $this->tmpl->assign('prev_img', $previous_img);
499 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
500 $this->tmpl->assign('next_img', $next_img);
502 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
503 $this->tmpl->assign('photo_number', $i);
504 $this->tmpl->assign('photo_count', count($all_photos));
506 $this->tmpl->show("single_photo.tpl");
511 * all available tags and tag cloud
513 * this function outputs all available tags (time ordered)
514 * and in addition output them as tag cloud (tags which have
515 * many photos will appears more then others)
517 public function getAvailableTags()
523 $result = $this->db->db_query("
524 SELECT tag_id as id, count(tag_id) as quantity
534 while($row = $this->db->db_fetch_object($result)) {
535 $tags[$row['id']] = $row['quantity'];
538 // change these font sizes if you will
539 $max_size = 125; // max font size in %
540 $min_size = 75; // min font size in %
542 // get the largest and smallest array values
543 $max_qty = max(array_values($tags));
544 $min_qty = min(array_values($tags));
546 // find the range of values
547 $spread = $max_qty - $min_qty;
548 if (0 == $spread) { // we don't want to divide by zero
552 // determine the font-size increment
553 // this is the increase per tag quantity (times used)
554 $step = ($max_size - $min_size)/($spread);
556 // loop through our tag array
557 foreach ($tags as $key => $value) {
559 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
562 // calculate CSS font-size
563 // find the $value in excess of $min_qty
564 // multiply by the font-size increment ($size)
565 // and add the $min_size set above
566 $size = $min_size + (($value - $min_qty) * $step);
567 // uncomment if you want sizes in whole %:
570 if(isset($this->tags[$key])) {
571 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
576 $output = substr($output, 0, strlen($output)-2);
579 } // getAvailableTags()
582 * output all selected tags
584 * this function output all tags which have been selected
585 * by the user. the selected tags are stored in the
586 * session-variable $_SESSION['selected_tags']
588 public function getSelectedTags()
593 foreach($this->avail_tags as $tag)
595 // return all selected tags
596 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
597 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
601 $output = substr($output, 0, strlen($output)-2);
604 } // getSelectedTags()
607 * add tag to users session variable
609 * this function will add the specified to users current
610 * tag selection. if a date search has been made before
611 * it will be now cleared
613 public function addTag($tag)
615 if(!isset($_SESSION['selected_tags']))
616 $_SESSION['selected_tags'] = Array();
618 if(!in_array($tag, $_SESSION['selected_tags']))
619 array_push($_SESSION['selected_tags'], $tag);
624 * remove tag to users session variable
626 * this function removes the specified tag from
627 * users current tag selection
629 public function delTag($tag)
631 if(isset($_SESSION['selected_tags'])) {
632 $key = array_search($tag, $_SESSION['selected_tags']);
633 unset($_SESSION['selected_tags'][$key]);
634 sort($_SESSION['selected_tags']);
640 * reset tag selection
642 * if there is any tag selection, it will be
645 public function resetTags()
647 if(isset($_SESSION['selected_tags']))
648 unset($_SESSION['selected_tags']);
655 * if a specific photo was requested (external link)
656 * unset the session variable now
658 public function resetPhotoView()
660 if(isset($_SESSION['current_photo']))
661 unset($_SESSION['current_photo']);
663 } // resetPhotoView();
668 * if any tag search has taken place, reset
671 public function resetTagSearch()
673 if(isset($_SESSION['searchfor']))
674 unset($_SESSION['searchfor']);
676 } // resetTagSearch()
681 * if any date search has taken place, reset
684 public function resetDateSearch()
686 if(isset($_SESSION['from_date']))
687 unset($_SESSION['from_date']);
688 if(isset($_SESSION['to_date']))
689 unset($_SESSION['to_date']);
691 } // resetDateSearch();
694 * return all photo according selection
696 * this function returns all photos based on
697 * the tag-selection, tag- or date-search.
698 * the tag-search also has to take care of AND
699 * and OR conjunctions
701 public function getPhotoSelection()
703 $matched_photos = Array();
705 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
706 $from_date = $_SESSION['from_date'];
707 $to_date = $_SESSION['to_date'];
708 $additional_where_cond = "
709 p.time>='". $from_date ."'
711 p.time<='". $to_date ."'
715 if(isset($_SESSION['sort_order'])) {
716 $order_str = $this->get_sort_order();
719 /* return a search result */
720 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
722 SELECT DISTINCT pt1.photo_id
724 INNER JOIN photo_tags pt2
725 ON pt1.photo_id=pt2.photo_id
732 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
734 if(isset($additional_where_cond))
735 $query_str.= "AND ". $additional_where_cond ." ";
737 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
738 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
741 if(isset($order_str))
742 $query_str.= $order_str;
744 $result = $this->db->db_query($query_str);
745 while($row = $this->db->db_fetch_object($result)) {
746 array_push($matched_photos, $row['photo_id']);
748 return $matched_photos;
751 /* return according the selected tags */
752 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
754 foreach($_SESSION['selected_tags'] as $tag)
755 $selected.= $tag .",";
756 $selected = substr($selected, 0, strlen($selected)-1);
758 /* photo has to match at least on of the selected tags */
759 if($_SESSION['tag_condition'] == 'or') {
761 SELECT DISTINCT pt1.photo_id
763 INNER JOIN photo_tags pt2
764 ON pt1.photo_id=pt2.photo_id
769 WHERE pt1.tag_id IN (". $selected .")
771 if(isset($additional_where_cond))
772 $query_str.= "AND ". $additional_where_cond ." ";
774 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
775 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
778 if(isset($order_str))
779 $query_str.= $order_str;
781 /* photo has to match all selected tags */
782 elseif($_SESSION['tag_condition'] == 'and') {
784 if(count($_SESSION['selected_tags']) >= 32) {
785 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
786 print "evaluate your tag selection. Please remove some tags from your selection.\n";
790 /* Join together a table looking like
792 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
794 so the query can quickly return all images matching the
795 selected tags in an AND condition
800 SELECT DISTINCT pt1.photo_id
804 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
811 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
813 INNER JOIN photo_tags pt". ($i+2) ."
814 ON pt1.photo_id=pt". ($i+2) .".photo_id
821 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
822 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
824 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
827 if(isset($additional_where_cond))
828 $query_str.= "AND ". $additional_where_cond;
830 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
831 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
834 if(isset($order_str))
835 $query_str.= $order_str;
839 $result = $this->db->db_query($query_str);
840 while($row = $this->db->db_fetch_object($result)) {
841 array_push($matched_photos, $row['photo_id']);
843 return $matched_photos;
846 /* return all available photos */
848 SELECT DISTINCT photo_id
855 if(isset($additional_where_cond))
856 $query_str.= "WHERE ". $additional_where_cond ." ";
858 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
859 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
862 if(isset($order_str))
863 $query_str.= $order_str;
865 $result = $this->db->db_query($query_str);
866 while($row = $this->db->db_fetch_object($result)) {
867 array_push($matched_photos, $row['photo_id']);
869 return $matched_photos;
871 } // getPhotoSelection()
874 * control HTML ouput for photo index
876 * this function provides all the necessary information
877 * for the photo index template.
879 public function showPhotoIndex()
881 $photos = $this->getPhotoSelection();
883 $count = count($photos);
885 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
886 $anchor = $_SESSION['begin_with'];
888 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
894 elseif($this->cfg->rows_per_page > 0) {
896 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
901 $begin_with = $_SESSION['begin_with'];
903 // verify $begin_with - perhaps the thumbs-per-rows or
904 // rows-per-page variables have changed or the jump back
905 // from a photo wasn't exact - so calculate the real new
907 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
908 for($i = 0; $i <= $count; $i+=$multiplicator) {
909 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
916 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
922 $images[$rows] = Array();
923 $img_height[$rows] = Array();
924 $img_width[$rows] = Array();
925 $img_id[$rows] = Array();
926 $img_name[$rows] = Array();
927 $img_title = Array();
929 for($i = $begin_with; $i < $end_with; $i++) {
931 if(isset($photos[$i])) {
933 $images[$rows][$cols] = $photos[$i];
934 $img_id[$rows][$cols] = $i;
935 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
936 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
938 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
940 if(file_exists($thumb_path)) {
941 $info = getimagesize($thumb_path);
942 $img_width[$rows][$cols] = $info[0];
943 $img_height[$rows][$cols] = $info[1];
946 if($cols == $this->cfg->thumbs_per_row-1) {
949 $images[$rows] = Array();
950 $img_width[$rows] = Array();
951 $img_height[$rows] = Array();
959 // +1 for for smarty's selection iteration
962 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
963 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
965 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
966 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
967 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
970 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
971 $this->tmpl->assign('tag_result', 1);
974 /* do we have to display the page selector ? */
975 if($this->cfg->rows_per_page != 0) {
979 /* calculate the page switchers */
980 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
981 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
984 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
985 if($end_with < $count)
986 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
988 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
989 $last_page = ceil($count / $photo_per_page);
991 /* get the current selected page */
992 if($begin_with == 0) {
996 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1003 for($i = 1; $i <= $last_page; $i++) {
1005 if($current_page == $i)
1006 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1007 elseif($current_page-1 == $i || $current_page+1 == $i)
1008 $style = "style=\"font-size: 105%;\"";
1009 elseif(($current_page-5 >= $i) && ($i != 1) ||
1010 ($current_page+5 <= $i) && ($i != $last_page))
1011 $style = "style=\"font-size: 75%;\"";
1015 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1018 $select.= ">". $i ."</a> ";
1020 // until 9 pages we show the selector from 1-9
1021 if($last_page <= 9) {
1022 $page_select.= $select;
1025 if($i == 1 /* first page */ ||
1026 $i == $last_page /* last page */ ||
1027 $i == $current_page /* current page */ ||
1028 $i == ceil($last_page * 0.25) /* first quater */ ||
1029 $i == ceil($last_page * 0.5) /* half */ ||
1030 $i == ceil($last_page * 0.75) /* third quater */ ||
1031 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1032 (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 */ ||
1033 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1034 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1036 $page_select.= $select;
1044 $page_select.= "......... ";
1049 /* only show the page selector if we have more then one page */
1051 $this->tmpl->assign('page_selector', $page_select);
1055 $current_tags = $this->getCurrentTags();
1056 $extern_link = "index.php?mode=showpi";
1057 $rss_link = "index.php?mode=rss";
1058 if($current_tags != "") {
1059 $extern_link.= "&tags=". $current_tags;
1060 $rss_link.= "&tags=". $current_tags;
1062 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1063 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1064 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1067 $export_link = "index.php?mode=export";
1068 $slideshow_link = "index.php?mode=slideshow";
1070 $this->tmpl->assign('extern_link', $extern_link);
1071 $this->tmpl->assign('slideshow_link', $slideshow_link);
1072 $this->tmpl->assign('export_link', $export_link);
1073 $this->tmpl->assign('rss_link', $rss_link);
1074 $this->tmpl->assign('count', $count);
1075 $this->tmpl->assign('width', $this->cfg->thumb_width);
1076 $this->tmpl->assign('images', $images);
1077 $this->tmpl->assign('img_width', $img_width);
1078 $this->tmpl->assign('img_height', $img_height);
1079 $this->tmpl->assign('img_id', $img_id);
1080 $this->tmpl->assign('img_name', $img_name);
1081 $this->tmpl->assign('img_title', $img_title);
1082 $this->tmpl->assign('rows', $rows);
1083 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1085 $this->tmpl->show("photo_index.tpl");
1088 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1090 } // showPhotoIndex()
1093 * show credit template
1095 public function showCredits()
1097 $this->tmpl->assign('version', $this->cfg->version);
1098 $this->tmpl->assign('product', $this->cfg->product);
1099 $this->tmpl->assign('db_version', $this->dbver);
1100 $this->tmpl->show("credits.tpl");
1105 * create_thumbnails for the requested width
1107 * this function creates image thumbnails of $orig_image
1108 * stored as $thumb_image. It will check if the image is
1109 * in a supported format, if necessary rotate the image
1110 * (based on EXIF orientation meta headers) and re-sizing.
1112 public function create_thumbnail($orig_image, $thumb_image, $width)
1114 if(!file_exists($orig_image)) {
1118 $details = getimagesize($orig_image);
1120 /* check if original photo is a support image type */
1121 if(!$this->checkifImageSupported($details['mime']))
1124 $meta = $this->get_meta_informations($orig_image);
1129 switch($meta['Orientation']) {
1131 case 1: /* top, left */
1132 $rotate = 0; $flip = false; break;
1133 case 2: /* top, right */
1134 $rotate = 0; $flip = true; break;
1135 case 3: /* bottom, left */
1136 $rotate = 180; $flip = false; break;
1137 case 4: /* bottom, right */
1138 $rotate = 180; $flip = true; break;
1139 case 5: /* left side, top */
1140 $rotate = 90; $flip = true; break;
1141 case 6: /* right side, top */
1142 $rotate = 90; $flip = false; break;
1143 case 7: /* left side, bottom */
1144 $rotate = 270; $flip = true; break;
1145 case 8: /* right side, bottom */
1146 $rotate = 270; $flip = false; break;
1149 $src_img = @imagecreatefromjpeg($orig_image);
1152 print "Can't load image from ". $orig_image ."\n";
1156 /* grabs the height and width */
1157 $cur_width = imagesx($src_img);
1158 $cur_height = imagesy($src_img);
1160 // If requested width is more then the actual image width,
1161 // do not generate a thumbnail, instead safe the original
1162 // as thumbnail but with lower quality
1164 if($width >= $cur_width) {
1165 $result = imagejpeg($src_img, $thumb_image, 75);
1166 imagedestroy($src_img);
1170 // If the image will be rotate because EXIF orientation said so
1171 // 'virtually rotate' the image for further calculations
1172 if($rotate == 90 || $rotate == 270) {
1174 $cur_width = $cur_height;
1178 /* calculates aspect ratio */
1179 $aspect_ratio = $cur_height / $cur_width;
1182 if($aspect_ratio < 1) {
1184 $new_h = abs($new_w * $aspect_ratio);
1186 /* 'virtually' rotate the image and calculate it's ratio */
1187 $tmp_w = $cur_height;
1188 $tmp_h = $cur_width;
1189 /* now get the ratio from the 'rotated' image */
1190 $tmp_ratio = $tmp_h/$tmp_w;
1191 /* now calculate the new dimensions */
1193 $tmp_h = abs($tmp_w * $tmp_ratio);
1195 // now that we know, how high they photo should be, if it
1196 // gets rotated, use this high to scale the image
1198 $new_w = abs($new_h / $aspect_ratio);
1200 // If the image will be rotate because EXIF orientation said so
1201 // now 'virtually rotate' back the image for the image manipulation
1202 if($rotate == 90 || $rotate == 270) {
1209 /* creates new image of that size */
1210 $dst_img = imagecreatetruecolor($new_w, $new_h);
1212 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1214 /* copies resized portion of original image into new image */
1215 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1217 /* needs the image to be flipped horizontal? */
1221 for($x = 0; $x < $new_w; $x++) {
1222 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1227 $this->_debug("(ROTATE)");
1228 $dst_img = $this->rotateImage($dst_img, $rotate);
1231 /* write down new generated file */
1232 $result = imagejpeg($dst_img, $thumb_image, 75);
1234 /* free your mind */
1235 imagedestroy($dst_img);
1236 imagedestroy($src_img);
1238 if($result === false) {
1239 print "Can't write thumbnail ". $thumb_image ."\n";
1245 } // create_thumbnail()
1248 * return all exif meta data from the file
1250 public function get_meta_informations($file)
1252 return exif_read_data($file);
1254 } // get_meta_informations()
1257 * create phpfspot own sqlite database
1259 * this function creates phpfspots own sqlite database
1260 * if it does not exist yet. this own is used to store
1261 * some necessary informations (md5 sum's, ...).
1263 public function check_config_table()
1265 // if the config table doesn't exist yet, create it
1266 if(!$this->cfg_db->db_check_table_exists("images")) {
1267 $this->cfg_db->db_exec("
1268 CREATE TABLE images (
1269 img_idx int primary key,
1275 } // check_config_table
1278 * Generates a thumbnail from photo idx
1280 * This function will generate JPEG thumbnails from provided F-Spot photo
1283 * 1. Check if all thumbnail generations (width) are already in place and
1285 * 2. Check if the md5sum of the original file has changed
1286 * 3. Generate the thumbnails if needed
1288 public function gen_thumb($idx = 0, $force = 0)
1292 $resolutions = Array(
1293 $this->cfg->thumb_width,
1294 $this->cfg->photo_width,
1295 $this->cfg->mini_width,
1298 /* get details from F-Spot's database */
1299 $details = $this->get_photo_details($idx);
1301 /* calculate file MD5 sum */
1302 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1304 if(!file_exists($full_path)) {
1305 $this->_error("File ". $full_path ." does not exist\n");
1309 if(!is_readable($full_path)) {
1310 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1314 $file_md5 = md5_file($full_path);
1316 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1320 foreach($resolutions as $resolution) {
1322 $thumb_sub_path = substr($file_md5, 0, 2);
1323 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1325 if(!file_exists(dirname($thumb_path))) {
1326 mkdir(dirname($thumb_path), 0755);
1329 /* if the thumbnail file doesn't exist, create it */
1330 if(!file_exists($thumb_path)) {
1332 $this->_debug(" ". $resolution ."px");
1333 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1338 /* if the file hasn't changed there is no need to regen the thumb */
1339 elseif($file_md5 != $this->getMD5($idx) || $force) {
1341 $this->_debug(" ". $resolution ."px");
1342 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1350 $this->_debug(" already exist");
1353 /* set the new/changed MD5 sum for the current photo */
1355 $this->setMD5($idx, $file_md5);
1358 $this->_debug("\n");
1363 * returns stored md5 sum for a specific photo
1365 * this function queries the phpfspot database for a
1366 * stored MD5 checksum of the specified photo
1368 public function getMD5($idx)
1370 $result = $this->cfg_db->db_query("
1373 WHERE img_idx='". $idx ."'
1379 $img = $this->cfg_db->db_fetch_object($result);
1380 return $img['img_md5'];
1385 * set MD5 sum for the specific photo
1387 private function setMD5($idx, $md5)
1389 $result = $this->cfg_db->db_exec("
1390 REPLACE INTO images (img_idx, img_md5)
1391 VALUES ('". $idx ."', '". $md5 ."')
1397 * store current tag condition
1399 * this function stores the current tag condition
1400 * (AND or OR) in the users session variables
1402 public function setTagCondition($mode)
1404 $_SESSION['tag_condition'] = $mode;
1406 } // setTagCondition()
1409 * invoke tag & date search
1411 * this function will return all matching tags and store
1412 * them in the session variable selected_tags. furthermore
1413 * it also handles the date search.
1414 * getPhotoSelection() will then only return the matching
1417 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1421 $_SESSION['searchfor'] = $searchfor;
1422 $_SESSION['sort_order'] = $sort_order;
1424 $_SESSION['from_date'] = strtotime($from);
1426 unset($_SESSION['from_date']);
1428 $_SESSION['to_date'] = strtotime($to);
1430 unset($_SESSION['to_date']);
1432 if($searchfor != "") {
1433 /* new search, reset the current selected tags */
1434 $_SESSION['selected_tags'] = Array();
1435 foreach($this->avail_tags as $tag) {
1436 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1437 array_push($_SESSION['selected_tags'], $tag);
1446 * this function rotates the image according the
1449 private function rotateImage($img, $degrees)
1451 if(function_exists("imagerotate")) {
1452 $img = imagerotate($img, $degrees, 0);
1454 function imagerotate($src_img, $angle)
1456 $src_x = imagesx($src_img);
1457 $src_y = imagesy($src_img);
1458 if ($angle == 180) {
1462 elseif ($src_x <= $src_y) {
1466 elseif ($src_x >= $src_y) {
1471 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1472 imagealphablending($rotate, false);
1477 for ($y = 0; $y < ($src_y); $y++) {
1478 for ($x = 0; $x < ($src_x); $x++) {
1479 $color = imagecolorat($src_img, $x, $y);
1480 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1486 for ($y = 0; $y < ($src_y); $y++) {
1487 for ($x = 0; $x < ($src_x); $x++) {
1488 $color = imagecolorat($src_img, $x, $y);
1489 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1495 for ($y = 0; $y < ($src_y); $y++) {
1496 for ($x = 0; $x < ($src_x); $x++) {
1497 $color = imagecolorat($src_img, $x, $y);
1498 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1512 $img = imagerotate($img, $degrees);
1521 * return all assigned tags for the specified photo
1523 private function get_photo_tags($idx)
1525 $result = $this->db->db_query("
1528 INNER JOIN photo_tags pt
1530 WHERE pt.photo_id='". $idx ."'
1535 while($row = $this->db->db_fetch_object($result))
1536 $tags[$row['id']] = $row['name'];
1540 } // get_photo_tags()
1543 * create on-the-fly images with text within
1545 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1547 if (strlen($color) != 6)
1550 $int = hexdec($color);
1551 $h = imagefontheight($font);
1552 $fw = imagefontwidth($font);
1553 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1554 $lines = count($txt);
1555 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1556 $bg = imagecolorallocate($im, 255, 255, 255);
1557 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1560 foreach ($txt as $text) {
1561 $x = (($w - ($fw * strlen($text))) / 2);
1562 imagestring($im, $font, $x, $y, $text, $color);
1563 $y += ($h + $space);
1566 Header("Content-type: image/png");
1569 } // showTextImage()
1572 * check if all requirements are met
1574 private function checkRequirements()
1576 if(!function_exists("imagecreatefromjpeg")) {
1577 print "PHP GD library extension is missing<br />\n";
1581 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1582 print "PHP SQLite3 library extension is missing<br />\n";
1586 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1587 ini_set('track_errors', 1);
1588 @include_once 'HTML/AJAX/Server.php';
1589 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1590 print "PEAR HTML_AJAX package is missing<br />\n";
1593 @include_once 'Calendar/Calendar.php';
1594 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1595 print "PEAR Calendar package is missing<br />\n";
1598 ini_restore('track_errors');
1605 } // checkRequirements()
1607 private function _debug($text)
1609 if($this->fromcmd) {
1616 * check if specified MIME type is supported
1618 public function checkifImageSupported($mime)
1620 if(in_array($mime, Array("image/jpeg")))
1625 } // checkifImageSupported()
1627 public function _error($text)
1629 switch($this->cfg->logging) {
1631 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1638 error_log($text, 3, $his->cfg->log_file);
1645 * output calendard input fields
1647 private function get_calendar($mode)
1649 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1650 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1651 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1653 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1654 if(!isset($_SESSION[$mode .'_date']))
1655 $output.= " disabled=\"disabled\"";
1657 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1658 if(!isset($_SESSION[$mode .'_date']))
1659 $output.= " disabled=\"disabled\"";
1661 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1662 if(!isset($_SESSION[$mode .'_date']))
1663 $output.= " disabled=\"disabled\"";
1671 * output calendar matrix
1673 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1675 if (!isset($year)) $year = date('Y');
1676 if (!isset($month)) $month = date('m');
1677 if (!isset($day)) $day = date('d');
1682 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1683 require_once CALENDAR_ROOT.'Day.php';
1686 $month = new Calendar_Month_Weekdays($year,$month);
1689 $prevStamp = $month->prevMonth(true);
1690 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1691 $nextStamp = $month->nextMonth(true);
1692 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1694 $selectedDays = array (
1695 new Calendar_Day($year,$month,$day),
1696 new Calendar_Day($year,12,25),
1699 // Build the days in the month
1700 $month->build($selectedDays);
1702 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1703 $this->tmpl->assign('prev_month', $prev);
1704 $this->tmpl->assign('next_month', $next);
1706 while ( $day = $month->fetch() ) {
1708 if(!isset($matrix[$rows]))
1709 $matrix[$rows] = Array();
1713 $dayStamp = $day->thisDay(true);
1714 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1716 // isFirst() to find start of week
1717 if ( $day->isFirst() )
1720 if ( $day->isSelected() ) {
1721 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1722 } else if ( $day->isEmpty() ) {
1723 $string.= "<td> </td>\n";
1725 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1728 // isLast() to find end of week
1729 if ( $day->isLast() )
1730 $string.= "</tr>\n";
1732 $matrix[$rows][$cols] = $string;
1742 $this->tmpl->assign('matrix', $matrix);
1743 $this->tmpl->assign('rows', $rows);
1744 $this->tmpl->show("calendar.tpl");
1746 } // get_calendar_matrix()
1749 * output export page
1751 public function getExport($mode)
1753 $pictures = $this->getPhotoSelection();
1754 $current_tags = $this->getCurrentTags();
1756 foreach($pictures as $picture) {
1758 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1759 if($current_tags != "") {
1760 $orig_url.= "&tags=". $current_tags;
1762 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1763 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1766 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1771 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1772 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1776 // "[%pictureurl% %thumbnailurl%]"
1777 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1780 case 'MoinMoinList':
1781 // " * [%pictureurl% %thumbnailurl%]"
1782 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1793 public function getRSSFeed()
1795 Header("Content-type: text/xml; charset=utf-8");
1796 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1799 xmlns:media="http://search.yahoo.com/mrss/"
1800 xmlns:dc="http://purl.org/dc/elements/1.1/"
1803 <title>phpfspot</title>
1804 <description>phpfspot RSS feed</description>
1805 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1806 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1807 <generator>phpfspot</generator>
1810 $pictures = $this->getPhotoSelection();
1811 $current_tags = $this->getCurrentTags();
1813 foreach($pictures as $picture) {
1815 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1816 if($current_tags != "") {
1817 $orig_url.= "&tags=". $current_tags;
1819 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1820 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1823 $details = $this->get_photo_details($picture);
1825 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1826 $thumb_html = htmlspecialchars("
1827 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1829 ". $details['description']);
1831 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1832 $meta = $this->get_meta_informations($orig_path);
1833 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1837 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1838 <link><?php print htmlspecialchars($orig_url); ?></link>
1839 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1840 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1842 <?php print $thumb_html; ?>
1844 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1859 * return all selected tags as one string
1861 private function getCurrentTags()
1864 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1865 foreach($_SESSION['selected_tags'] as $tag)
1866 $current_tags.= $tag .",";
1867 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1869 return $current_tags;
1871 } // getCurrentTags()
1874 * return the current photo
1876 public function getCurrentPhoto()
1878 if(isset($_SESSION['current_photo'])) {
1879 print $_SESSION['current_photo'];
1881 } // getCurrentPhoto()
1884 * tells the client browser what to do
1886 * this function is getting called via AJAX by the
1887 * client browsers. it will tell them what they have
1888 * to do next. This is necessary for directly jumping
1889 * into photo index or single photo view when the are
1890 * requested with specific URLs
1892 public function whatToDo()
1894 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1895 return "show_photo";
1897 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1898 return "showpi_tags";
1900 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1904 return "nothing special";
1909 * return the current process-user
1911 private function getuid()
1913 if($uid = posix_getuid()) {
1914 if($user = posix_getpwuid($uid)) {
1915 return $user['name'];
1924 * returns a select-dropdown box to select photo index sort parameters
1926 private function get_sort_field()
1928 $output = "<select name=\"sort_order\">";
1929 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1930 $output.= "<option value=\"". $sort_order ."\"";
1931 if($sort_order == $_SESSION['sort_order']) {
1932 $output.= " selected=\"selected\"";
1934 $output.= ">". $sort_order ."</option>";
1936 $output.= "</select>";
1939 } // get_sort_field()
1942 * returns the currently selected sort order
1944 private function get_sort_order()
1946 switch($_SESSION['sort_order']) {
1948 return " ORDER BY p.time ASC";
1951 return " ORDER BY p.time DESC";
1954 return " ORDER BY p.name ASC";
1957 return " ORDER BY p.name DESC";
1961 } // get_sort_order()
1964 * return the next to be shown slide show image
1966 * this function returns the URL of the next image
1967 * in the slideshow sequence.
1969 public function getNextSlideShowImage()
1971 $all_photos = $this->getPhotoSelection();
1973 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1974 $_SESSION['slideshow_img'] = 0;
1976 $_SESSION['slideshow_img']++;
1978 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1980 } // getNextSlideShowImage()
1983 * return the previous to be shown slide show image
1985 * this function returns the URL of the previous image
1986 * in the slideshow sequence.
1988 public function getPrevSlideShowImage()
1990 $all_photos = $this->getPhotoSelection();
1992 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1993 $_SESSION['slideshow_img'] = 0;
1995 $_SESSION['slideshow_img']--;
1997 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1999 } // getPrevSlideShowImage()
2001 public function resetSlideShow()
2003 if(isset($_SESSION['slideshow_img']))
2004 unset($_SESSION['slideshow_img']);
2005 } // resetSlideShow()
2010 * this function will get all photos from the fspot
2011 * database and randomly return ONE entry
2013 * saddly there is yet no sqlite3 function which returns
2014 * the bulk result in array, so we have to fill up our
2017 public function get_random_photo()
2021 $result = $this->db->db_query("
2026 while($row = $this->db->db_fetch_object($result)) {
2027 array_push($all, $row['id']);
2030 return $all[array_rand($all)];
2032 } // get_random_photo()
2035 * validates provided date
2037 * this function validates if the provided date
2038 * contains a valid date and will return true
2041 public function isValidDate($date_str)
2043 $timestamp = strtotime($date_str);
2045 if(is_numeric($timestamp))
2053 * timestamp to string conversion
2055 private function ts2str($timestamp)
2057 return strftime("%Y-%m-%d", $timestamp);
2060 private function extractTags($tags_str)
2062 $not_validated = split(',', $_GET['tags']);
2063 $validated = array();
2065 foreach($not_validated as $tag) {
2066 if(is_numeric($tag))
2067 array_push($validated, $tag);
2075 * returns the full path to a thumbnail
2077 public function get_thumb_path($width, $photo)
2079 $md5 = $this->getMD5($photo);
2080 $sub_path = substr($md5, 0, 2);
2081 return $this->cfg->thumb_path
2089 } // get_thumb_path()
2092 * returns server's virtual host name
2094 private function get_server_name()
2096 return $_SERVER['SERVER_NAME'];
2097 } // get_server_name()
2100 * returns type of webprotocol which is
2103 private function get_web_protocol()
2105 if(!isset($_SERVER['HTTPS']))
2109 } // get_web_protocol()
2112 * return url to this phpfspot installation
2114 private function get_phpfspot_url()
2116 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2117 } // get_phpfspot_url()
2120 * check file exists and is readable
2122 * returns true, if everything is ok, otherwise false
2123 * if $silent is not set, this function will output and
2126 private function check_readable($file, $silent = null)
2128 if(!file_exists($file)) {
2130 print "File \"". $file ."\" does not exist.\n";
2134 if(!is_readable($file)) {
2136 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2142 } // check_readable()
2145 * check if all needed indices are present
2147 * this function checks, if some needed indices are already
2148 * present, or if not, create them on the fly. they are
2149 * necessary to speed up some queries like that one look for
2150 * all tags, when show_tags is specified in the configuration.
2152 private function checkDbIndices()
2154 $result = $this->db->db_exec("
2155 CREATE INDEX IF NOT EXISTS
2162 } // checkDbIndices()
2165 * retrive F-Spot database version
2167 * this function will return the F-Spot database version number
2168 * It is stored within the sqlite3 database in the table meta
2170 public function getFspotDBVersion()
2172 if($result = $this->db->db_fetchSingleRow("
2173 SELECT data as version
2176 name LIKE 'F-Spot Database Version'
2178 return $result['version'];
2182 } // getFspotDBVersion()
2185 * parse the provided URI and will returned the
2188 public function parse_uri($uri, $mode)
2190 if(($components = parse_url($uri)) !== false) {
2194 return basename($components['path']);
2197 return dirname($components['path']);
2200 return $components['path'];