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";
39 * this function will be called on class construct
40 * and will check requirements, loads configuration,
41 * open databases and start the user session
43 public function __construct()
45 $this->cfg = new PHPFSPOT_CFG;
47 /* set application name and version information */
48 $this->cfg->product = "phpfspot";
49 $this->cfg->version = "1.2";
51 /* Check necessary requirements */
52 if(!$this->checkRequirements()) {
56 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
57 if(!is_writeable($this->cfg->fspot_db)) {
58 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
62 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
63 print dirname($this->cfg->phpfspot_db) .": directory is not writeable!";
67 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
68 if(!is_writeable($this->cfg->phpfspot_db)) {
69 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
72 $this->check_config_table();
74 /* include Smarty template engine */
75 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
78 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
79 /* overload Smarty class if our own template handler */
80 require_once "phpfspot_tmpl.php";
81 $this->tmpl = new PHPFSPOT_TMPL($this);
83 /* check if all necessary indices exist */
84 $this->checkDbIndices();
88 if(!isset($_SESSION['tag_condition']))
89 $_SESSION['tag_condition'] = 'or';
91 if(!isset($_SESSION['sort_order']))
92 $_SESSION['sort_order'] = 'date_asc';
94 if(!isset($_SESSION['searchfor']))
95 $_SESSION['searchfor'] = '';
97 // if begin_with is still set but rows_per_page is now 0, unset it
98 if(isset($_SESSION['begin_with']) && $this->cfg->rows_per_page == 0)
99 unset($_SESSION['begin_with']);
103 public function __destruct()
109 * show - generate html output
111 * this function can be called after the constructor has
112 * prepared everyhing. it will load the index.tpl smarty
113 * template. if necessary it will registere pre-selects
114 * (photo index, photo, tag search, date search) into
117 public function show()
119 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
120 $this->tmpl->assign('page_title', $this->cfg->page_title);
121 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
122 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
124 $_SESSION['start_action'] = $_GET['mode'];
126 switch($_GET['mode']) {
128 if(isset($_GET['tags'])) {
129 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
131 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
132 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
134 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
135 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
139 if(isset($_GET['tags'])) {
140 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
141 $_SESSION['start_action'] = 'showp';
143 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
144 $_SESSION['current_photo'] = $_GET['id'];
145 $_SESSION['start_action'] = 'showp';
147 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
148 $_SESSION['from_date'] = strtotime($_GET['from_date']);
150 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
151 $_SESSION['to_date'] = strtotime($_GET['to_date']);
155 $this->tmpl->show("export.tpl");
159 $this->tmpl->show("slideshow.tpl");
163 if(isset($_GET['tags'])) {
164 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
166 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
167 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
169 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
170 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
177 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
178 $this->tmpl->assign('date_search_enabled', true);
180 $this->tmpl->assign('from_date', $this->get_calendar('from'));
181 $this->tmpl->assign('to_date', $this->get_calendar('to'));
182 $this->tmpl->assign('sort_field', $this->get_sort_field());
183 $this->tmpl->assign('content_page', 'welcome.tpl');
184 $this->tmpl->show("index.tpl");
189 * get_tags - grab all tags of f-spot's database
191 * this function will get all available tags from
192 * the f-spot database and store them within two
193 * arrays within this class for later usage. in
194 * fact, if the user requests (hide_tags) it will
195 * opt-out some of them.
197 * this function is getting called once by show()
199 private function get_tags()
201 $this->avail_tags = Array();
204 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
207 DISTINCT t1.id as id, t1.name as name
210 INNER JOIN photo_tags
211 pt2 ON pt1.photo_id=pt2.photo_id
217 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
219 t1.sort_priority ASC";
221 $result = $this->db->db_query($query_str);
225 $result = $this->db->db_query("
228 ORDER BY sort_priority ASC
232 while($row = $this->db->db_fetch_object($result)) {
234 $tag_id = $row['id'];
235 $tag_name = $row['name'];
237 /* if the user has specified to ignore this tag in phpfspot's
238 configuration, ignore it here so it does not get added to
241 if(in_array($row['name'], $this->cfg->hide_tags))
244 /* if you include the following if-clause and the user has specified
245 to only show certain tags which are specified in phpfspot's
246 configuration, ignore all others so they will not be added to the
248 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
249 !in_array($row['name'], $this->cfg->show_tags))
253 $this->tags[$tag_id] = $tag_name;
254 $this->avail_tags[$count] = $tag_id;
262 * extract all photo details
264 * retrieve all available details from f-spot's
265 * database and return them as object
267 public function get_photo_details($idx)
270 SELECT p.id, p.name, p.time, p.directory_path, p.description
274 /* if show_tags is set, only return details for photos which
275 are specified to be shown
277 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
279 INNER JOIN photo_tags pt
283 WHERE p.id='". $idx ."'
284 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
288 WHERE p.id='". $idx ."'
292 $result = $this->db->db_query($query_str);
293 return $this->db->db_fetch_object($result);
295 } // get_photo_details
298 * returns aligned photo names
300 * this function returns aligned (length) names for
301 * an specific photo. If the length of the name exceeds
302 * $limit the name will be shrinked (...)
304 public function getPhotoName($idx, $limit = 0)
306 if($details = $this->get_photo_details($idx)) {
307 $name = $this->shrink_text($details['name'], $limit);
314 * shrink text according provided limit
316 * If the length of the name exceeds $limit the
317 * text will be shortend and some content in between
318 * will be replaced with "..."
320 private function shrink_text($text, $limit)
322 if($limit != 0 && strlen($text) > $limit) {
323 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
331 * translate f-spoth photo path
333 * as the full-qualified path recorded in the f-spot database
334 * is usally not the same as on the webserver, this function
335 * will replace the path with that one specified in the cfg
337 public function translate_path($path, $width = 0)
339 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
344 * control HTML ouput for a single photo
346 * this function provides all the necessary information
347 * for the single photo template.
349 public function showPhoto($photo)
351 /* get all photos from the current photo selection */
352 $all_photos = $this->getPhotoSelection();
353 $count = count($all_photos);
355 for($i = 0; $i < $count; $i++) {
357 // $get_next will be set, when the photo which has to
358 // be displayed has been found - this means that the
359 // next available is in fact the NEXT image (for the
361 if(isset($get_next)) {
362 $next_img = $all_photos[$i];
366 /* the next photo is our NEXT photo */
367 if($all_photos[$i] == $photo) {
371 $previous_img = $all_photos[$i];
374 if($photo == $all_photos[$i]) {
379 $details = $this->get_photo_details($photo);
386 $orig_path = $this->translate_path($details['directory_path']) ."/". $details['name'];
387 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
389 if(!file_exists($orig_path)) {
390 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
393 if(!is_readable($orig_path)) {
394 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
397 /* If the thumbnail doesn't exist yet, try to create it */
398 if(!file_exists($thumb_path)) {
399 $this->gen_thumb($photo, true);
400 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
403 /* get f-spot database meta information */
404 $meta = $this->get_meta_informations($orig_path);
406 /* If EXIF data are available, use them */
407 if(isset($meta['ExifImageWidth'])) {
408 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
410 $info = getimagesize($orig_path);
411 $meta_res = $info[0] ."x". $info[1];
414 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
415 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
416 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
418 $extern_link = "index.php?mode=showp&id=". $photo;
419 $current_tags = $this->getCurrentTags();
420 if($current_tags != "") {
421 $extern_link.= "&tags=". $current_tags;
423 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
424 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
427 $this->tmpl->assign('extern_link', $extern_link);
429 if(file_exists($thumb_path)) {
431 $info = getimagesize($thumb_path);
433 $this->tmpl->assign('description', $details['description']);
434 $this->tmpl->assign('image_name', $details['name']);
436 $this->tmpl->assign('width', $info[0]);
437 $this->tmpl->assign('height', $info[1]);
438 $this->tmpl->assign('ExifMadeOn', $meta_date);
439 $this->tmpl->assign('ExifMadeWith', $meta_make);
440 $this->tmpl->assign('ExifOrigResolution', $meta_res);
441 $this->tmpl->assign('ExifFileSize', $meta_size);
443 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
444 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
446 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
447 $this->tmpl->assign('current', $current);
450 $this->_error("Can't open file ". $thumb_path ."\n");
455 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
456 $this->tmpl->assign('prev_img', $previous_img);
460 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
461 $this->tmpl->assign('next_img', $next_img);
463 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
464 $this->tmpl->assign('photo_number', $i);
465 $this->tmpl->assign('photo_count', count($all_photos));
467 $this->tmpl->show("single_photo.tpl");
472 * all available tags and tag cloud
474 * this function outputs all available tags (time ordered)
475 * and in addition output them as tag cloud (tags which have
476 * many photos will appears more then others)
478 public function getAvailableTags()
484 $result = $this->db->db_query("
485 SELECT tag_id as id, count(tag_id) as quantity
495 while($row = $this->db->db_fetch_object($result)) {
496 $tags[$row['id']] = $row['quantity'];
499 // change these font sizes if you will
500 $max_size = 125; // max font size in %
501 $min_size = 75; // min font size in %
503 // get the largest and smallest array values
504 $max_qty = max(array_values($tags));
505 $min_qty = min(array_values($tags));
507 // find the range of values
508 $spread = $max_qty - $min_qty;
509 if (0 == $spread) { // we don't want to divide by zero
513 // determine the font-size increment
514 // this is the increase per tag quantity (times used)
515 $step = ($max_size - $min_size)/($spread);
517 // loop through our tag array
518 foreach ($tags as $key => $value) {
520 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
523 // calculate CSS font-size
524 // find the $value in excess of $min_qty
525 // multiply by the font-size increment ($size)
526 // and add the $min_size set above
527 $size = $min_size + (($value - $min_qty) * $step);
528 // uncomment if you want sizes in whole %:
531 if(isset($this->tags[$key])) {
532 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
537 $output = substr($output, 0, strlen($output)-2);
540 } // getAvailableTags()
543 * output all selected tags
545 * this function output all tags which have been selected
546 * by the user. the selected tags are stored in the
547 * session-variable $_SESSION['selected_tags']
549 public function getSelectedTags()
554 foreach($this->avail_tags as $tag)
556 // return all selected tags
557 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
558 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
562 $output = substr($output, 0, strlen($output)-2);
565 } // getSelectedTags()
568 * add tag to users session variable
570 * this function will add the specified to users current
571 * tag selection. if a date search has been made before
572 * it will be now cleared
574 public function addTag($tag)
576 if(!isset($_SESSION['selected_tags']))
577 $_SESSION['selected_tags'] = Array();
579 if(!in_array($tag, $_SESSION['selected_tags']))
580 array_push($_SESSION['selected_tags'], $tag);
585 * remove tag to users session variable
587 * this function removes the specified tag from
588 * users current tag selection
590 public function delTag($tag)
592 if(isset($_SESSION['selected_tags'])) {
593 $key = array_search($tag, $_SESSION['selected_tags']);
594 unset($_SESSION['selected_tags'][$key]);
595 sort($_SESSION['selected_tags']);
601 * reset tag selection
603 * if there is any tag selection, it will be
606 public function resetTags()
608 if(isset($_SESSION['selected_tags']))
609 unset($_SESSION['selected_tags']);
616 * if a specific photo was requested (external link)
617 * unset the session variable now
619 public function resetPhotoView()
621 if(isset($_SESSION['current_photo']))
622 unset($_SESSION['current_photo']);
624 } // resetPhotoView();
629 * if any tag search has taken place, reset
632 public function resetTagSearch()
634 if(isset($_SESSION['searchfor']))
635 unset($_SESSION['searchfor']);
637 } // resetTagSearch()
642 * if any date search has taken place, reset
645 public function resetDateSearch()
647 if(isset($_SESSION['from_date']))
648 unset($_SESSION['from_date']);
649 if(isset($_SESSION['to_date']))
650 unset($_SESSION['to_date']);
652 } // resetDateSearch();
655 * return all photo according selection
657 * this function returns all photos based on
658 * the tag-selection, tag- or date-search.
659 * the tag-search also has to take care of AND
660 * and OR conjunctions
662 public function getPhotoSelection()
664 $matched_photos = Array();
666 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
667 $from_date = $_SESSION['from_date'];
668 $to_date = $_SESSION['to_date'];
669 $additional_where_cond = "
670 p.time>='". $from_date ."'
672 p.time<='". $to_date ."'
676 if(isset($_SESSION['sort_order'])) {
677 $order_str = $this->get_sort_order();
680 /* return a search result */
681 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
683 SELECT DISTINCT pt1.photo_id
685 INNER JOIN photo_tags pt2
686 ON pt1.photo_id=pt2.photo_id
693 WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
695 if(isset($additional_where_cond))
696 $query_str.= "AND ". $additional_where_cond ." ";
698 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
699 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
702 if(isset($order_str))
703 $query_str.= $order_str;
705 $result = $this->db->db_query($query_str);
706 while($row = $this->db->db_fetch_object($result)) {
707 array_push($matched_photos, $row['photo_id']);
709 return $matched_photos;
712 /* return according the selected tags */
713 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
715 foreach($_SESSION['selected_tags'] as $tag)
716 $selected.= $tag .",";
717 $selected = substr($selected, 0, strlen($selected)-1);
719 /* photo has to match at least on of the selected tags */
720 if($_SESSION['tag_condition'] == 'or') {
722 SELECT DISTINCT pt1.photo_id
724 INNER JOIN photo_tags pt2
725 ON pt1.photo_id=pt2.photo_id
730 WHERE pt1.tag_id IN (". $selected .")
732 if(isset($additional_where_cond))
733 $query_str.= "AND ". $additional_where_cond ." ";
735 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
736 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
739 if(isset($order_str))
740 $query_str.= $order_str;
742 /* photo has to match all selected tags */
743 elseif($_SESSION['tag_condition'] == 'and') {
745 if(count($_SESSION['selected_tags']) >= 32) {
746 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
747 print "evaluate your tag selection. Please remove some tags from your selection.\n";
751 /* Join together a table looking like
753 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
755 so the query can quickly return all images matching the
756 selected tags in an AND condition
761 SELECT DISTINCT pt1.photo_id
765 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
772 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
774 INNER JOIN photo_tags pt". ($i+2) ."
775 ON pt1.photo_id=pt". ($i+2) .".photo_id
782 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
783 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
785 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
788 if(isset($additional_where_cond))
789 $query_str.= "AND ". $additional_where_cond;
791 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
792 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
795 if(isset($order_str))
796 $query_str.= $order_str;
800 $result = $this->db->db_query($query_str);
801 while($row = $this->db->db_fetch_object($result)) {
802 array_push($matched_photos, $row['photo_id']);
804 return $matched_photos;
807 /* return all available photos */
809 SELECT DISTINCT photo_id
816 if(isset($additional_where_cond))
817 $query_str.= "WHERE ". $additional_where_cond ." ";
819 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
820 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
823 if(isset($order_str))
824 $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;
832 } // getPhotoSelection()
835 * control HTML ouput for photo index
837 * this function provides all the necessary information
838 * for the photo index template.
840 public function showPhotoIndex()
842 $photos = $this->getPhotoSelection();
844 $count = count($photos);
846 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
847 $anchor = $_SESSION['begin_with'];
849 if(!isset($this->cfg->rows_per_page) || $this->cfg->rows_per_page == 0) {
855 elseif($this->cfg->rows_per_page > 0) {
857 if(!$_SESSION['begin_with'] || $_SESSION['begin_with'] == 0)
861 $begin_with = $_SESSION['begin_with'];
863 // verify $begin_with - perhaps the thumbs-per-rows or
864 // rows-per-page variables have changed or the jump back
865 // from a photo wasn't exact - so calculate the real new
867 $multiplicator = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
868 for($i = 0; $i <= $count; $i+=$multiplicator) {
869 if($begin_with >= $i && $begin_with < $i+$multiplicator) {
876 $end_with = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
882 $images[$rows] = Array();
883 $img_height[$rows] = Array();
884 $img_width[$rows] = Array();
885 $img_id[$rows] = Array();
886 $img_name[$rows] = Array();
887 $img_title = Array();
889 for($i = $begin_with; $i < $end_with; $i++) {
891 $images[$rows][$cols] = $photos[$i];
892 $img_id[$rows][$cols] = $i;
893 $img_name[$rows][$cols] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
894 $img_title[$rows][$cols] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
896 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
898 if(file_exists($thumb_path)) {
899 $info = getimagesize($thumb_path);
900 $img_width[$rows][$cols] = $info[0];
901 $img_height[$rows][$cols] = $info[1];
904 if($cols == $this->cfg->thumbs_per_row-1) {
907 $images[$rows] = Array();
908 $img_width[$rows] = Array();
909 $img_height[$rows] = Array();
916 // +1 for for smarty's selection iteration
919 if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
920 $this->tmpl->assign('searchfor', $_SESSION['searchfor']);
922 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
923 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
924 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
927 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
928 $this->tmpl->assign('tag_result', 1);
931 /* do we have to display the page selector ? */
932 if($this->cfg->rows_per_page != 0) {
934 /* calculate the page switchers */
935 $previous_start = $begin_with - ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
936 $next_start = $begin_with + ($this->cfg->rows_per_page * $this->cfg->thumbs_per_row);
939 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
940 if($end_with < $count)
941 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
943 $photo_per_page = $this->cfg->rows_per_page * $this->cfg->thumbs_per_row;
944 $last_page = ceil($count / $photo_per_page);
946 /* get the current selected page */
947 if($begin_with == 0) {
951 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
958 for($i = 1; $i <= $last_page; $i++) {
960 if($current_page == $i)
961 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
962 elseif($current_page-1 == $i || $current_page+1 == $i)
963 $style = "style=\"font-size: 105%;\"";
964 elseif(($current_page-5 >= $i) && ($i != 1) ||
965 ($current_page+5 <= $i) && ($i != $last_page))
966 $style = "style=\"font-size: 75%;\"";
970 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
973 $select.= ">". $i ."</a> ";
975 // until 9 pages we show the selector from 1-9
976 if($last_page <= 9) {
977 $page_select.= $select;
980 if($i == 1 /* first page */ ||
981 $i == $last_page /* last page */ ||
982 $i == $current_page /* current page */ ||
983 $i == ceil($last_page * 0.25) /* first quater */ ||
984 $i == ceil($last_page * 0.5) /* half */ ||
985 $i == ceil($last_page * 0.75) /* third quater */ ||
986 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
987 (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 */ ||
988 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
989 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
991 $page_select.= $select;
999 $page_select.= "......... ";
1004 /* only show the page selector if we have more then one page */
1006 $this->tmpl->assign('page_selector', $page_select);
1010 $current_tags = $this->getCurrentTags();
1011 $extern_link = "index.php?mode=showpi";
1012 $rss_link = "index.php?mode=rss";
1013 if($current_tags != "") {
1014 $extern_link.= "&tags=". $current_tags;
1015 $rss_link.= "&tags=". $current_tags;
1017 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1018 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1019 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1022 $export_link = "index.php?mode=export";
1023 $slideshow_link = "index.php?mode=slideshow";
1025 $this->tmpl->assign('extern_link', $extern_link);
1026 $this->tmpl->assign('slideshow_link', $slideshow_link);
1027 $this->tmpl->assign('export_link', $export_link);
1028 $this->tmpl->assign('rss_link', $rss_link);
1029 $this->tmpl->assign('count', $count);
1030 $this->tmpl->assign('width', $this->cfg->thumb_width);
1031 $this->tmpl->assign('images', $images);
1032 $this->tmpl->assign('img_width', $img_width);
1033 $this->tmpl->assign('img_height', $img_height);
1034 $this->tmpl->assign('img_id', $img_id);
1035 $this->tmpl->assign('img_name', $img_name);
1036 $this->tmpl->assign('img_title', $img_title);
1037 $this->tmpl->assign('rows', $rows);
1038 $this->tmpl->assign('columns', $this->cfg->thumbs_per_row);
1040 $this->tmpl->show("photo_index.tpl");
1043 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1045 } // showPhotoIndex()
1048 * show credit template
1050 public function showCredits()
1052 $this->tmpl->assign('version', $this->cfg->version);
1053 $this->tmpl->assign('product', $this->cfg->product);
1054 $this->tmpl->show("credits.tpl");
1059 * create_thumbnails for the requested width
1061 * this function creates image thumbnails of $orig_image
1062 * stored as $thumb_image. It will check if the image is
1063 * in a supported format, if necessary rotate the image
1064 * (based on EXIF orientation meta headers) and re-sizing.
1066 public function create_thumbnail($orig_image, $thumb_image, $width)
1068 if(!file_exists($orig_image)) {
1072 $details = getimagesize($orig_image);
1074 /* check if original photo is a support image type */
1075 if(!$this->checkifImageSupported($details['mime']))
1078 $meta = $this->get_meta_informations($orig_image);
1083 switch($meta['Orientation']) {
1085 case 1: /* top, left */
1086 $rotate = 0; $flip = false; break;
1087 case 2: /* top, right */
1088 $rotate = 0; $flip = true; break;
1089 case 3: /* bottom, left */
1090 $rotate = 180; $flip = false; break;
1091 case 4: /* bottom, right */
1092 $rotate = 180; $flip = true; break;
1093 case 5: /* left side, top */
1094 $rotate = 90; $flip = true; break;
1095 case 6: /* right side, top */
1096 $rotate = 90; $flip = false; break;
1097 case 7: /* left side, bottom */
1098 $rotate = 270; $flip = true; break;
1099 case 8: /* right side, bottom */
1100 $rotate = 270; $flip = false; break;
1103 $src_img = @imagecreatefromjpeg($orig_image);
1106 print "Can't load image from ". $orig_image ."\n";
1110 /* grabs the height and width */
1111 $cur_width = imagesx($src_img);
1112 $cur_height = imagesy($src_img);
1114 // If requested width is more then the actual image width,
1115 // do not generate a thumbnail, instead safe the original
1116 // as thumbnail but with lower quality
1118 if($width >= $cur_width) {
1119 $result = imagejpeg($src_img, $thumb_image, 75);
1120 imagedestroy($src_img);
1124 // If the image will be rotate because EXIF orientation said so
1125 // 'virtually rotate' the image for further calculations
1126 if($rotate == 90 || $rotate == 270) {
1128 $cur_width = $cur_height;
1132 /* calculates aspect ratio */
1133 $aspect_ratio = $cur_height / $cur_width;
1136 if($aspect_ratio < 1) {
1138 $new_h = abs($new_w * $aspect_ratio);
1140 /* 'virtually' rotate the image and calculate it's ratio */
1141 $tmp_w = $cur_height;
1142 $tmp_h = $cur_width;
1143 /* now get the ratio from the 'rotated' image */
1144 $tmp_ratio = $tmp_h/$tmp_w;
1145 /* now calculate the new dimensions */
1147 $tmp_h = abs($tmp_w * $tmp_ratio);
1149 // now that we know, how high they photo should be, if it
1150 // gets rotated, use this high to scale the image
1152 $new_w = abs($new_h / $aspect_ratio);
1154 // If the image will be rotate because EXIF orientation said so
1155 // now 'virtually rotate' back the image for the image manipulation
1156 if($rotate == 90 || $rotate == 270) {
1163 /* creates new image of that size */
1164 $dst_img = imagecreatetruecolor($new_w, $new_h);
1166 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1168 /* copies resized portion of original image into new image */
1169 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1171 /* needs the image to be flipped horizontal? */
1175 for($x = 0; $x < $new_w; $x++) {
1176 imagecopy($dst_img, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1181 $this->_debug("(ROTATE)");
1182 $dst_img = $this->rotateImage($dst_img, $rotate);
1185 /* write down new generated file */
1186 $result = imagejpeg($dst_img, $thumb_image, 75);
1188 /* free your mind */
1189 imagedestroy($dst_img);
1190 imagedestroy($src_img);
1192 if($result === false) {
1193 print "Can't write thumbnail ". $thumb_image ."\n";
1199 } // create_thumbnail()
1202 * return all exif meta data from the file
1204 public function get_meta_informations($file)
1206 return exif_read_data($file);
1208 } // get_meta_informations()
1211 * create phpfspot own sqlite database
1213 * this function creates phpfspots own sqlite database
1214 * if it does not exist yet. this own is used to store
1215 * some necessary informations (md5 sum's, ...).
1217 public function check_config_table()
1219 // if the config table doesn't exist yet, create it
1220 if(!$this->cfg_db->db_check_table_exists("images")) {
1221 $this->cfg_db->db_exec("
1222 CREATE TABLE images (
1223 img_idx int primary key,
1229 } // check_config_table
1232 * Generates a thumbnail from photo idx
1234 * This function will generate JPEG thumbnails from provided F-Spot photo
1237 * 1. Check if all thumbnail generations (width) are already in place and
1239 * 2. Check if the md5sum of the original file has changed
1240 * 3. Generate the thumbnails if needed
1242 public function gen_thumb($idx = 0, $force = 0)
1246 $resolutions = Array(
1247 $this->cfg->thumb_width,
1248 $this->cfg->photo_width,
1249 $this->cfg->mini_width,
1252 /* get details from F-Spot's database */
1253 $details = $this->get_photo_details($idx);
1255 /* calculate file MD5 sum */
1256 $full_path = $this->translate_path($details['directory_path']) ."/". $details['name'];
1258 if(!file_exists($full_path)) {
1259 $this->_error("File ". $full_path ." does not exist\n");
1263 if(!is_readable($full_path)) {
1264 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1268 $file_md5 = md5_file($full_path);
1270 $this->_debug("Image [". $idx ."] ". $this->shrink_text($details['name'], 20) ." Thumbnails:");
1274 foreach($resolutions as $resolution) {
1276 $thumb_sub_path = substr($file_md5, 0, 2);
1277 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1279 if(!file_exists(dirname($thumb_path))) {
1280 mkdir(dirname($thumb_path), 0755);
1283 /* if the thumbnail file doesn't exist, create it */
1284 if(!file_exists($thumb_path)) {
1286 $this->_debug(" ". $resolution ."px");
1287 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1292 /* if the file hasn't changed there is no need to regen the thumb */
1293 elseif($file_md5 != $this->getMD5($idx) || $force) {
1295 $this->_debug(" ". $resolution ."px");
1296 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1304 $this->_debug(" already exist");
1307 /* set the new/changed MD5 sum for the current photo */
1309 $this->setMD5($idx, $file_md5);
1312 $this->_debug("\n");
1317 * returns stored md5 sum for a specific photo
1319 * this function queries the phpfspot database for a
1320 * stored MD5 checksum of the specified photo
1322 public function getMD5($idx)
1324 $result = $this->cfg_db->db_query("
1327 WHERE img_idx='". $idx ."'
1333 $img = $this->cfg_db->db_fetch_object($result);
1334 return $img['img_md5'];
1339 * set MD5 sum for the specific photo
1341 private function setMD5($idx, $md5)
1343 $result = $this->cfg_db->db_exec("
1344 REPLACE INTO images (img_idx, img_md5)
1345 VALUES ('". $idx ."', '". $md5 ."')
1351 * store current tag condition
1353 * this function stores the current tag condition
1354 * (AND or OR) in the users session variables
1356 public function setTagCondition($mode)
1358 $_SESSION['tag_condition'] = $mode;
1360 } // setTagCondition()
1363 * invoke tag & date search
1365 * this function will return all matching tags and store
1366 * them in the session variable selected_tags. furthermore
1367 * it also handles the date search.
1368 * getPhotoSelection() will then only return the matching
1371 public function startSearch($searchfor, $sort_order, $from = 0, $to = 0)
1375 $_SESSION['searchfor'] = $searchfor;
1376 $_SESSION['sort_order'] = $sort_order;
1378 $_SESSION['from_date'] = strtotime($from);
1380 unset($_SESSION['from_date']);
1382 $_SESSION['to_date'] = strtotime($to);
1384 unset($_SESSION['to_date']);
1386 if($searchfor != "") {
1387 /* new search, reset the current selected tags */
1388 $_SESSION['selected_tags'] = Array();
1389 foreach($this->avail_tags as $tag) {
1390 if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
1391 array_push($_SESSION['selected_tags'], $tag);
1400 * this function rotates the image according the
1403 private function rotateImage($img, $degrees)
1405 if(function_exists("imagerotate")) {
1406 $img = imagerotate($img, $degrees, 0);
1408 function imagerotate($src_img, $angle)
1410 $src_x = imagesx($src_img);
1411 $src_y = imagesy($src_img);
1412 if ($angle == 180) {
1416 elseif ($src_x <= $src_y) {
1420 elseif ($src_x >= $src_y) {
1425 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1426 imagealphablending($rotate, false);
1431 for ($y = 0; $y < ($src_y); $y++) {
1432 for ($x = 0; $x < ($src_x); $x++) {
1433 $color = imagecolorat($src_img, $x, $y);
1434 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1440 for ($y = 0; $y < ($src_y); $y++) {
1441 for ($x = 0; $x < ($src_x); $x++) {
1442 $color = imagecolorat($src_img, $x, $y);
1443 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1449 for ($y = 0; $y < ($src_y); $y++) {
1450 for ($x = 0; $x < ($src_x); $x++) {
1451 $color = imagecolorat($src_img, $x, $y);
1452 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1466 $img = imagerotate($img, $degrees);
1475 * return all assigned tags for the specified photo
1477 private function get_photo_tags($idx)
1479 $result = $this->db->db_query("
1482 INNER JOIN photo_tags pt
1484 WHERE pt.photo_id='". $idx ."'
1489 while($row = $this->db->db_fetch_object($result))
1490 $tags[$row['id']] = $row['name'];
1494 } // get_photo_tags()
1497 * create on-the-fly images with text within
1499 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1501 if (strlen($color) != 6)
1504 $int = hexdec($color);
1505 $h = imagefontheight($font);
1506 $fw = imagefontwidth($font);
1507 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1508 $lines = count($txt);
1509 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1510 $bg = imagecolorallocate($im, 255, 255, 255);
1511 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1514 foreach ($txt as $text) {
1515 $x = (($w - ($fw * strlen($text))) / 2);
1516 imagestring($im, $font, $x, $y, $text, $color);
1517 $y += ($h + $space);
1520 Header("Content-type: image/png");
1523 } // showTextImage()
1526 * check if all requirements are met
1528 private function checkRequirements()
1530 if(!function_exists("imagecreatefromjpeg")) {
1531 print "PHP GD library extension is missing<br />\n";
1535 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1536 print "PHP SQLite3 library extension is missing<br />\n";
1540 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1541 ini_set('track_errors', 1);
1542 @include_once 'HTML/AJAX/Server.php';
1543 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1544 print "PEAR HTML_AJAX package is missing<br />\n";
1547 @include_once 'Calendar/Calendar.php';
1548 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1549 print "PEAR Calendar package is missing<br />\n";
1552 ini_restore('track_errors');
1559 } // checkRequirements()
1561 private function _debug($text)
1563 if($this->fromcmd) {
1570 * check if specified MIME type is supported
1572 public function checkifImageSupported($mime)
1574 if(in_array($mime, Array("image/jpeg")))
1579 } // checkifImageSupported()
1581 public function _error($text)
1583 switch($this->cfg->logging) {
1585 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1592 error_log($text, 3, $his->cfg->log_file);
1599 * output calendard input fields
1601 private function get_calendar($mode)
1603 $year = $_SESSION[$mode .'_date'] ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1604 $month = $_SESSION[$mode .'_date'] ? date("m", $_SESSION[$mode .'_date']) : date("m");
1605 $day = $_SESSION[$mode .'_date'] ? date("d", $_SESSION[$mode .'_date']) : date("d");
1607 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1608 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1610 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1611 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1613 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1614 if(!isset($_SESSION[$mode .'_date'])) $output.= " disabled=\"disabled\"";
1621 * output calendar matrix
1623 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1625 if (!isset($year)) $year = date('Y');
1626 if (!isset($month)) $month = date('m');
1627 if (!isset($day)) $day = date('d');
1632 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1633 require_once CALENDAR_ROOT.'Day.php';
1636 $month = new Calendar_Month_Weekdays($year,$month);
1639 $prevStamp = $month->prevMonth(true);
1640 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1641 $nextStamp = $month->nextMonth(true);
1642 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1644 $selectedDays = array (
1645 new Calendar_Day($year,$month,$day),
1646 new Calendar_Day($year,12,25),
1649 // Build the days in the month
1650 $month->build($selectedDays);
1652 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1653 $this->tmpl->assign('prev_month', $prev);
1654 $this->tmpl->assign('next_month', $next);
1656 while ( $day = $month->fetch() ) {
1658 if(!isset($matrix[$rows]))
1659 $matrix[$rows] = Array();
1663 $dayStamp = $day->thisDay(true);
1664 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1666 // isFirst() to find start of week
1667 if ( $day->isFirst() )
1670 if ( $day->isSelected() ) {
1671 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1672 } else if ( $day->isEmpty() ) {
1673 $string.= "<td> </td>\n";
1675 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1678 // isLast() to find end of week
1679 if ( $day->isLast() )
1680 $string.= "</tr>\n";
1682 $matrix[$rows][$cols] = $string;
1692 $this->tmpl->assign('matrix', $matrix);
1693 $this->tmpl->assign('rows', $rows);
1694 $this->tmpl->show("calendar.tpl");
1696 } // get_calendar_matrix()
1699 * output export page
1701 public function getExport($mode)
1703 $pictures = $this->getPhotoSelection();
1704 $current_tags = $this->getCurrentTags();
1706 foreach($pictures as $picture) {
1708 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1709 if($current_tags != "") {
1710 $orig_url.= "&tags=". $current_tags;
1712 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1713 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1716 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1721 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1722 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1726 // "[%pictureurl% %thumbnailurl%]"
1727 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1730 case 'MoinMoinList':
1731 // " * [%pictureurl% %thumbnailurl%]"
1732 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1743 public function getRSSFeed()
1745 Header("Content-type: text/xml; charset=utf-8");
1746 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1749 xmlns:media="http://search.yahoo.com/mrss/"
1750 xmlns:dc="http://purl.org/dc/elements/1.1/"
1753 <title>phpfspot</title>
1754 <description>phpfspot RSS feed</description>
1755 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1756 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1757 <generator>phpfspot</generator>
1760 $pictures = $this->getPhotoSelection();
1761 $current_tags = $this->getCurrentTags();
1763 foreach($pictures as $picture) {
1765 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1766 if($current_tags != "") {
1767 $orig_url.= "&tags=". $current_tags;
1769 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1770 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1773 $details = $this->get_photo_details($picture);
1775 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1776 $thumb_html = htmlspecialchars("
1777 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1779 ". $details['description']);
1781 $orig_path = $this->translate_path($details['directory_path']) ."/". $details['name'];
1782 $meta = $this->get_meta_informations($orig_path);
1783 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1787 <title><?php print htmlspecialchars($details['name']); ?></title>
1788 <link><?php print htmlspecialchars($orig_url); ?></link>
1789 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1790 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1792 <?php print $thumb_html; ?>
1794 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1809 * return all selected tags as one string
1811 private function getCurrentTags()
1814 if($_SESSION['selected_tags'] != "") {
1815 foreach($_SESSION['selected_tags'] as $tag)
1816 $current_tags.= $tag .",";
1817 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1819 return $current_tags;
1821 } // getCurrentTags()
1824 * return the current photo
1826 public function getCurrentPhoto()
1828 if(isset($_SESSION['current_photo'])) {
1829 print $_SESSION['current_photo'];
1831 } // getCurrentPhoto()
1834 * tells the client browser what to do
1836 * this function is getting called via AJAX by the
1837 * client browsers. it will tell them what they have
1838 * to do next. This is necessary for directly jumping
1839 * into photo index or single photo view when the are
1840 * requested with specific URLs
1842 public function whatToDo()
1844 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
1845 return "show_photo";
1847 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1848 return "showpi_tags";
1850 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
1854 return "nothing special";
1859 * return the current process-user
1861 private function getuid()
1863 if($uid = posix_getuid()) {
1864 if($user = posix_getpwuid($uid)) {
1865 return $user['name'];
1874 * returns a select-dropdown box to select photo index sort parameters
1876 private function get_sort_field()
1878 $output = "<select name=\"sort_order\">";
1879 foreach(array('date_asc', 'date_desc', 'name_asc', 'name_desc') as $sort_order) {
1880 $output.= "<option value=\"". $sort_order ."\"";
1881 if($sort_order == $_SESSION['sort_order']) {
1882 $output.= " selected=\"selected\"";
1884 $output.= ">". $sort_order ."</option>";
1886 $output.= "</select>";
1889 } // get_sort_field()
1892 * returns the currently selected sort order
1894 private function get_sort_order()
1896 switch($_SESSION['sort_order']) {
1898 return " ORDER BY p.time ASC";
1901 return " ORDER BY p.time DESC";
1904 return " ORDER BY p.name ASC";
1907 return " ORDER BY p.name DESC";
1911 } // get_sort_order()
1914 * return the next to be shown slide show image
1916 * this function returns the URL of the next image
1917 * in the slideshow sequence.
1919 public function getNextSlideShowImage()
1921 $all_photos = $this->getPhotoSelection();
1923 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
1924 $_SESSION['slideshow_img'] = 0;
1926 $_SESSION['slideshow_img']++;
1928 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1930 } // getNextSlideShowImage()
1933 * return the previous to be shown slide show image
1935 * this function returns the URL of the previous image
1936 * in the slideshow sequence.
1938 public function getPrevSlideShowImage()
1940 $all_photos = $this->getPhotoSelection();
1942 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
1943 $_SESSION['slideshow_img'] = 0;
1945 $_SESSION['slideshow_img']--;
1947 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
1949 } // getPrevSlideShowImage()
1951 public function resetSlideShow()
1953 if(isset($_SESSION['slideshow_img']))
1954 unset($_SESSION['slideshow_img']);
1955 } // resetSlideShow()
1960 * this function will get all photos from the fspot
1961 * database and randomly return ONE entry
1963 * saddly there is yet no sqlite3 function which returns
1964 * the bulk result in array, so we have to fill up our
1967 public function get_random_photo()
1971 $result = $this->db->db_query("
1976 while($row = $this->db->db_fetch_object($result)) {
1977 array_push($all, $row['id']);
1980 return $all[array_rand($all)];
1982 } // get_random_photo()
1985 * validates provided date
1987 * this function validates if the provided date
1988 * contains a valid date and will return true
1991 public function isValidDate($date_str)
1993 $timestamp = strtotime($date_str);
1995 if(is_numeric($timestamp))
2003 * timestamp to string conversion
2005 private function ts2str($timestamp)
2007 return strftime("%Y-%m-%d", $timestamp);
2010 private function extractTags($tags_str)
2012 $not_validated = split(',', $_GET['tags']);
2013 $validated = array();
2015 foreach($not_validated as $tag) {
2016 if(is_numeric($tag))
2017 array_push($validated, $tag);
2025 * returns the full path to a thumbnail
2027 public function get_thumb_path($width, $photo)
2029 $md5 = $this->getMD5($photo);
2030 $sub_path = substr($md5, 0, 2);
2031 return $this->cfg->thumb_path
2039 } // get_thumb_path()
2042 * returns server's virtual host name
2044 private function get_server_name()
2046 return $_SERVER['SERVER_NAME'];
2047 } // get_server_name()
2050 * returns type of webprotocol which is
2053 private function get_web_protocol()
2055 if(!isset($_SERVER['HTTPS']))
2059 } // get_web_protocol()
2062 * return url to this phpfspot installation
2064 private function get_phpfspot_url()
2066 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2067 } // get_phpfspot_url()
2070 * check file exists and is readable
2072 * returns true, if everything is ok, otherwise false
2073 * if $silent is not set, this function will output and
2076 private function check_readable($file, $silent = null)
2078 if(!file_exists($file)) {
2080 print "File \"". $file ."\" does not exist.\n";
2084 if(!is_readable($file)) {
2086 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2092 } // check_readable()
2095 * check if all needed indices are present
2097 * this function checks, if some needed indices are already
2098 * present, or if not, create them on the fly. they are
2099 * necessary to speed up some queries like that one look for
2100 * all tags, when show_tags is specified in the configuration.
2102 private function checkDbIndices()
2104 $result = $this->db->db_exec("
2105 CREATE INDEX IF NOT EXISTS
2112 } // checkDbIndices()