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";
36 private $runtime_error = false;
42 * this function will be called on class construct
43 * and will check requirements, loads configuration,
44 * open databases and start the user session
46 public function __construct()
48 $this->cfg = new PHPFSPOT_CFG;
50 /* verify config settings */
51 if($this->check_config_options()) {
55 /* set application name and version information */
56 $this->cfg->product = "phpfspot";
57 $this->cfg->version = "1.3";
59 $this->sort_orders= array(
60 'date_asc' => 'Date ↑',
61 'date_desc' => 'Date ↓',
62 'name_asc' => 'Name ↑',
63 'name_desc' => 'Name ↓',
64 'tags_asc' => 'Tags ↑',
65 'tags_desc' => 'Tags ↓',
68 /* Check necessary requirements */
69 if(!$this->checkRequirements()) {
73 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
74 if(!is_writeable($this->cfg->fspot_db)) {
75 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
79 $this->dbver = $this->getFspotDBVersion();
81 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
82 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
86 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
87 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
91 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
92 if(!is_writeable($this->cfg->phpfspot_db)) {
93 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
97 $this->check_config_table();
99 /* include Smarty template engine */
100 if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
103 require $this->cfg->smarty_path .'/libs/Smarty.class.php';
104 /* overload Smarty class if our own template handler */
105 require_once "phpfspot_tmpl.php";
106 $this->tmpl = new PHPFSPOT_TMPL($this);
108 /* check if all necessary indices exist */
109 $this->checkDbIndices();
111 /* if session is not yet started, do it now */
112 if(session_id() == "")
115 if(!isset($_SESSION['tag_condition']))
116 $_SESSION['tag_condition'] = 'or';
118 if(!isset($_SESSION['sort_order']))
119 $_SESSION['sort_order'] = 'date_asc';
121 if(!isset($_SESSION['searchfor_tag']))
122 $_SESSION['searchfor_tag'] = '';
124 // if begin_with is still set but thumbs_per_page is now 0, unset it
125 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
126 unset($_SESSION['begin_with']);
130 public function __destruct()
136 * show - generate html output
138 * this function can be called after the constructor has
139 * prepared everyhing. it will load the index.tpl smarty
140 * template. if necessary it will registere pre-selects
141 * (photo index, photo, tag search, date search) into
144 public function show()
146 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
147 $this->tmpl->assign('page_title', $this->cfg->page_title);
148 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
149 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
151 if(isset($_GET['mode'])) {
153 $_SESSION['start_action'] = $_GET['mode'];
155 switch($_GET['mode']) {
157 if(isset($_GET['tags'])) {
158 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
160 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
161 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
163 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
164 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
168 if(isset($_GET['tags'])) {
169 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
170 $_SESSION['start_action'] = 'showp';
172 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
173 $_SESSION['current_photo'] = $_GET['id'];
174 $_SESSION['start_action'] = 'showp';
176 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
177 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
179 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
180 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
184 $this->tmpl->show("export.tpl");
188 $this->tmpl->show("slideshow.tpl");
192 if(isset($_GET['tags'])) {
193 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
195 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
196 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
198 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
199 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
207 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
208 $this->tmpl->assign('date_search_enabled', true);
210 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
211 $this->tmpl->assign('from_date', $this->get_calendar('from'));
212 $this->tmpl->assign('to_date', $this->get_calendar('to'));
213 $this->tmpl->assign('content_page', 'welcome.tpl');
214 $this->tmpl->show("index.tpl");
219 * get_tags - grab all tags of f-spot's database
221 * this function will get all available tags from
222 * the f-spot database and store them within two
223 * arrays within this class for later usage. in
224 * fact, if the user requests (hide_tags) it will
225 * opt-out some of them.
227 * this function is getting called once by show()
229 private function get_tags()
231 $this->avail_tags = Array();
234 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
237 DISTINCT t1.id as id, t1.name as name
240 INNER JOIN photo_tags
241 pt2 ON pt1.photo_id=pt2.photo_id
247 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
249 t1.sort_priority ASC";
251 $result = $this->db->db_query($query_str);
255 $result = $this->db->db_query("
258 ORDER BY sort_priority ASC
262 while($row = $this->db->db_fetch_object($result)) {
264 $tag_id = $row['id'];
265 $tag_name = $row['name'];
267 /* if the user has specified to ignore this tag in phpfspot's
268 configuration, ignore it here so it does not get added to
271 if(in_array($row['name'], $this->cfg->hide_tags))
274 /* if you include the following if-clause and the user has specified
275 to only show certain tags which are specified in phpfspot's
276 configuration, ignore all others so they will not be added to the
278 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
279 !in_array($row['name'], $this->cfg->show_tags))
283 $this->tags[$tag_id] = $tag_name;
284 $this->avail_tags[$count] = $tag_id;
292 * extract all photo details
294 * retrieve all available details from f-spot's
295 * database and return them as object
297 public function get_photo_details($idx)
299 if($this->dbver < 9) {
301 SELECT p.id, p.name, p.time, p.directory_path, p.description
307 SELECT p.id, p.uri, p.time, p.description
312 /* if show_tags is set, only return details for photos which
313 are specified to be shown
315 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
317 INNER JOIN photo_tags pt
321 WHERE p.id='". $idx ."'
322 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
326 WHERE p.id='". $idx ."'
330 if($result = $this->db->db_query($query_str)) {
332 $row = $this->db->db_fetch_object($result);
334 if($this->dbver < 9) {
335 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
344 } // get_photo_details
347 * returns aligned photo names
349 * this function returns aligned (length) names for
350 * an specific photo. If the length of the name exceeds
351 * $limit the name will be shrinked (...)
353 public function getPhotoName($idx, $limit = 0)
355 if($details = $this->get_photo_details($idx)) {
356 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
357 $name = $this->shrink_text($long_name, $limit);
367 * shrink text according provided limit
369 * If the length of the name exceeds $limit the
370 * text will be shortend and some content in between
371 * will be replaced with "..."
373 private function shrink_text($text, $limit)
375 if($limit != 0 && strlen($text) > $limit) {
376 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
384 * translate f-spoth photo path
386 * as the full-qualified path recorded in the f-spot database
387 * is usally not the same as on the webserver, this function
388 * will replace the path with that one specified in the cfg
390 public function translate_path($path, $width = 0)
392 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
397 * control HTML ouput for a single photo
399 * this function provides all the necessary information
400 * for the single photo template.
402 public function showPhoto($photo)
404 /* get all photos from the current photo selection */
405 $all_photos = $this->getPhotoSelection();
406 $count = count($all_photos);
408 for($i = 0; $i < $count; $i++) {
410 // $get_next will be set, when the photo which has to
411 // be displayed has been found - this means that the
412 // next available is in fact the NEXT image (for the
414 if(isset($get_next)) {
415 $next_img = $all_photos[$i];
419 /* the next photo is our NEXT photo */
420 if($all_photos[$i] == $photo) {
424 $previous_img = $all_photos[$i];
427 if($photo == $all_photos[$i]) {
432 $details = $this->get_photo_details($photo);
439 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
440 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
442 if(!file_exists($orig_path)) {
443 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
447 if(!is_readable($orig_path)) {
448 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
452 /* If the thumbnail doesn't exist yet, try to create it */
453 if(!file_exists($thumb_path)) {
454 $this->gen_thumb($photo, true);
455 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
458 /* get f-spot database meta information */
459 $meta = $this->get_meta_informations($orig_path);
461 /* If EXIF data are available, use them */
462 if(isset($meta['ExifImageWidth'])) {
463 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
465 $info = getimagesize($orig_path);
466 $meta_res = $info[0] ."x". $info[1];
469 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
470 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
471 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
473 $extern_link = "index.php?mode=showp&id=". $photo;
474 $current_tags = $this->getCurrentTags();
475 if($current_tags != "") {
476 $extern_link.= "&tags=". $current_tags;
478 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
479 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
482 $this->tmpl->assign('extern_link', $extern_link);
484 if(!file_exists($thumb_path)) {
485 $this->_error("Can't open file ". $thumb_path ."\n");
489 $info = getimagesize($thumb_path);
491 $this->tmpl->assign('description', $details['description']);
492 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
494 $this->tmpl->assign('width', $info[0]);
495 $this->tmpl->assign('height', $info[1]);
496 $this->tmpl->assign('ExifMadeOn', $meta_date);
497 $this->tmpl->assign('ExifMadeWith', $meta_make);
498 $this->tmpl->assign('ExifOrigResolution', $meta_res);
499 $this->tmpl->assign('ExifFileSize', $meta_size);
501 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
502 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
503 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
505 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
506 $this->tmpl->assign('current', $current);
509 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
510 $this->tmpl->assign('prev_img', $previous_img);
514 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
515 $this->tmpl->assign('next_img', $next_img);
517 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
518 $this->tmpl->assign('photo_number', $i);
519 $this->tmpl->assign('photo_count', count($all_photos));
521 $this->tmpl->show("single_photo.tpl");
526 * all available tags and tag cloud
528 * this function outputs all available tags (time ordered)
529 * and in addition output them as tag cloud (tags which have
530 * many photos will appears more then others)
532 public function getAvailableTags()
534 /* retrive tags from database */
539 $result = $this->db->db_query("
540 SELECT tag_id as id, count(tag_id) as quantity
550 while($row = $this->db->db_fetch_object($result)) {
551 $tags[$row['id']] = $row['quantity'];
554 // change these font sizes if you will
555 $max_size = 125; // max font size in %
556 $min_size = 75; // min font size in %
558 // get the largest and smallest array values
559 $max_qty = max(array_values($tags));
560 $min_qty = min(array_values($tags));
562 // find the range of values
563 $spread = $max_qty - $min_qty;
564 if (0 == $spread) { // we don't want to divide by zero
568 // determine the font-size increment
569 // this is the increase per tag quantity (times used)
570 $step = ($max_size - $min_size)/($spread);
572 // loop through our tag array
573 foreach ($tags as $key => $value) {
575 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
578 // calculate CSS font-size
579 // find the $value in excess of $min_qty
580 // multiply by the font-size increment ($size)
581 // and add the $min_size set above
582 $size = $min_size + (($value - $min_qty) * $step);
583 // uncomment if you want sizes in whole %:
586 if(isset($this->tags[$key])) {
587 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
592 $output = substr($output, 0, strlen($output)-2);
595 } // getAvailableTags()
598 * output all selected tags
600 * this function output all tags which have been selected
601 * by the user. the selected tags are stored in the
602 * session-variable $_SESSION['selected_tags']
604 public function getSelectedTags()
606 /* retrive tags from database */
611 foreach($this->avail_tags as $tag)
613 // return all selected tags
614 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
615 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
620 $output = substr($output, 0, strlen($output)-2);
624 return "no tags selected";
627 } // getSelectedTags()
630 * add tag to users session variable
632 * this function will add the specified to users current
633 * tag selection. if a date search has been made before
634 * it will be now cleared
636 public function addTag($tag)
638 if(!isset($_SESSION['selected_tags']))
639 $_SESSION['selected_tags'] = Array();
641 if(isset($_SESSION['searchfor_tag']))
642 unset($_SESSION['searchfor_tag']);
644 if(!in_array($tag, $_SESSION['selected_tags']))
645 array_push($_SESSION['selected_tags'], $tag);
650 * remove tag to users session variable
652 * this function removes the specified tag from
653 * users current tag selection
655 public function delTag($tag)
657 if(isset($_SESSION['searchfor_tag']))
658 unset($_SESSION['searchfor_tag']);
660 if(isset($_SESSION['selected_tags'])) {
661 $key = array_search($tag, $_SESSION['selected_tags']);
662 unset($_SESSION['selected_tags'][$key]);
663 sort($_SESSION['selected_tags']);
669 * reset tag selection
671 * if there is any tag selection, it will be
674 public function resetTags()
676 if(isset($_SESSION['selected_tags']))
677 unset($_SESSION['selected_tags']);
684 * if a specific photo was requested (external link)
685 * unset the session variable now
687 public function resetPhotoView()
689 if(isset($_SESSION['current_photo']))
690 unset($_SESSION['current_photo']);
692 } // resetPhotoView();
697 * if any tag search has taken place, reset it now
699 public function resetTagSearch()
701 if(isset($_SESSION['searchfor_tag']))
702 unset($_SESSION['searchfor_tag']);
704 } // resetTagSearch()
709 * if any name search has taken place, reset it now
711 public function resetNameSearch()
713 if(isset($_SESSION['searchfor_name']))
714 unset($_SESSION['searchfor_name']);
716 } // resetNameSearch()
721 * if any date search has taken place, reset
724 public function resetDateSearch()
726 if(isset($_SESSION['from_date']))
727 unset($_SESSION['from_date']);
728 if(isset($_SESSION['to_date']))
729 unset($_SESSION['to_date']);
731 } // resetDateSearch();
734 * return all photo according selection
736 * this function returns all photos based on
737 * the tag-selection, tag- or date-search.
738 * the tag-search also has to take care of AND
739 * and OR conjunctions
741 public function getPhotoSelection()
743 $matched_photos = Array();
745 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
746 $from_date = $_SESSION['from_date'];
747 $to_date = $_SESSION['to_date'];
748 $additional_where_cond = "
749 p.time>='". $from_date ."'
751 p.time<='". $to_date ."'
755 if(isset($_SESSION['searchfor_name'])) {
756 if($this->dbver < 9) {
757 $additional_where_cond.= "
759 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
761 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
766 $additional_where_cond.= "
768 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
770 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
776 if(isset($_SESSION['sort_order'])) {
777 $order_str = $this->get_sort_order();
780 /* return a search result */
781 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
783 SELECT DISTINCT pt1.photo_id
785 INNER JOIN photo_tags pt2
786 ON pt1.photo_id=pt2.photo_id
793 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
795 if(isset($additional_where_cond))
796 $query_str.= "AND ". $additional_where_cond ." ";
798 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
799 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
802 if(isset($order_str))
803 $query_str.= $order_str;
805 $result = $this->db->db_query($query_str);
806 while($row = $this->db->db_fetch_object($result)) {
807 array_push($matched_photos, $row['photo_id']);
809 return $matched_photos;
812 /* return according the selected tags */
813 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
815 foreach($_SESSION['selected_tags'] as $tag)
816 $selected.= $tag .",";
817 $selected = substr($selected, 0, strlen($selected)-1);
819 /* photo has to match at least on of the selected tags */
820 if($_SESSION['tag_condition'] == 'or') {
822 SELECT DISTINCT pt1.photo_id
824 INNER JOIN photo_tags pt2
825 ON pt1.photo_id=pt2.photo_id
830 WHERE pt1.tag_id IN (". $selected .")
832 if(isset($additional_where_cond))
833 $query_str.= "AND ". $additional_where_cond ." ";
835 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
836 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
839 if(isset($order_str))
840 $query_str.= $order_str;
842 /* photo has to match all selected tags */
843 elseif($_SESSION['tag_condition'] == 'and') {
845 if(count($_SESSION['selected_tags']) >= 32) {
846 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
847 print "evaluate your tag selection. Please remove some tags from your selection.\n";
851 /* Join together a table looking like
853 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
855 so the query can quickly return all images matching the
856 selected tags in an AND condition
861 SELECT DISTINCT pt1.photo_id
865 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
872 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
874 INNER JOIN photo_tags pt". ($i+2) ."
875 ON pt1.photo_id=pt". ($i+2) .".photo_id
882 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
883 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
885 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
888 if(isset($additional_where_cond))
889 $query_str.= "AND ". $additional_where_cond;
891 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
892 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
895 if(isset($order_str))
896 $query_str.= $order_str;
900 $result = $this->db->db_query($query_str);
901 while($row = $this->db->db_fetch_object($result)) {
902 array_push($matched_photos, $row['photo_id']);
904 return $matched_photos;
907 /* return all available photos */
911 LEFT JOIN photo_tags pt
917 if(isset($additional_where_cond))
918 $query_str.= "WHERE ". $additional_where_cond ." ";
920 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
921 if(isset($additional_where_cond))
922 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
924 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
927 if(isset($order_str))
928 $query_str.= $order_str;
930 $result = $this->db->db_query($query_str);
931 while($row = $this->db->db_fetch_object($result)) {
932 array_push($matched_photos, $row['id']);
934 return $matched_photos;
936 } // getPhotoSelection()
939 * control HTML ouput for photo index
941 * this function provides all the necessary information
942 * for the photo index template.
944 public function showPhotoIndex()
946 $photos = $this->getPhotoSelection();
948 $count = count($photos);
950 if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
951 $anchor = $_SESSION['begin_with'];
953 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
959 elseif($this->cfg->thumbs_per_page > 0) {
961 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
965 $begin_with = $_SESSION['begin_with'];
968 $end_with = $begin_with + $this->cfg->thumbs_per_page;
972 $images[$thumbs] = Array();
973 $img_height[$thumbs] = Array();
974 $img_width[$thumbs] = Array();
975 $img_id[$thumbs] = Array();
976 $img_name[$thumbs] = Array();
977 $img_title = Array();
979 for($i = $begin_with; $i < $end_with; $i++) {
981 if(isset($photos[$i])) {
983 $images[$thumbs] = $photos[$i];
984 $img_id[$thumbs] = $i;
985 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
986 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
988 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
990 if(file_exists($thumb_path)) {
991 $info = getimagesize($thumb_path);
992 $img_width[$thumbs] = $info[0];
993 $img_height[$thumbs] = $info[1];
999 // +1 for for smarty's selection iteration
1002 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1003 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1005 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1006 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1007 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1010 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1011 $this->tmpl->assign('tag_result', 1);
1014 /* do we have to display the page selector ? */
1015 if($this->cfg->thumbs_per_page != 0) {
1019 /* calculate the page switchers */
1020 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1021 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1023 if($begin_with != 0)
1024 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1025 if($end_with < $count)
1026 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1028 $photo_per_page = $this->cfg->thumbs_per_page;
1029 $last_page = ceil($count / $photo_per_page);
1031 /* get the current selected page */
1032 if($begin_with == 0) {
1036 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1043 for($i = 1; $i <= $last_page; $i++) {
1045 if($current_page == $i)
1046 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1047 elseif($current_page-1 == $i || $current_page+1 == $i)
1048 $style = "style=\"font-size: 105%;\"";
1049 elseif(($current_page-5 >= $i) && ($i != 1) ||
1050 ($current_page+5 <= $i) && ($i != $last_page))
1051 $style = "style=\"font-size: 75%;\"";
1055 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1058 $select.= ">". $i ."</a> ";
1060 // until 9 pages we show the selector from 1-9
1061 if($last_page <= 9) {
1062 $page_select.= $select;
1065 if($i == 1 /* first page */ ||
1066 $i == $last_page /* last page */ ||
1067 $i == $current_page /* current page */ ||
1068 $i == ceil($last_page * 0.25) /* first quater */ ||
1069 $i == ceil($last_page * 0.5) /* half */ ||
1070 $i == ceil($last_page * 0.75) /* third quater */ ||
1071 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1072 (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 */ ||
1073 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1074 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1076 $page_select.= $select;
1084 $page_select.= "......... ";
1089 /* only show the page selector if we have more then one page */
1091 $this->tmpl->assign('page_selector', $page_select);
1095 $current_tags = $this->getCurrentTags();
1096 $extern_link = "index.php?mode=showpi";
1097 $rss_link = "index.php?mode=rss";
1098 if($current_tags != "") {
1099 $extern_link.= "&tags=". $current_tags;
1100 $rss_link.= "&tags=". $current_tags;
1102 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1103 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1104 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1107 $export_link = "index.php?mode=export";
1108 $slideshow_link = "index.php?mode=slideshow";
1110 $this->tmpl->assign('extern_link', $extern_link);
1111 $this->tmpl->assign('slideshow_link', $slideshow_link);
1112 $this->tmpl->assign('export_link', $export_link);
1113 $this->tmpl->assign('rss_link', $rss_link);
1114 $this->tmpl->assign('count', $count);
1115 $this->tmpl->assign('width', $this->cfg->thumb_width);
1116 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1117 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1118 $this->tmpl->assign('images', $images);
1119 $this->tmpl->assign('img_width', $img_width);
1120 $this->tmpl->assign('img_height', $img_height);
1121 $this->tmpl->assign('img_id', $img_id);
1122 $this->tmpl->assign('img_name', $img_name);
1123 $this->tmpl->assign('img_title', $img_title);
1124 $this->tmpl->assign('thumbs', $thumbs);
1126 $this->tmpl->show("photo_index.tpl");
1129 print "<script language=\"JavaScript\">self.location.hash = '#image". $anchor ."';</script>\n";
1131 } // showPhotoIndex()
1134 * show credit template
1136 public function showCredits()
1138 $this->tmpl->assign('version', $this->cfg->version);
1139 $this->tmpl->assign('product', $this->cfg->product);
1140 $this->tmpl->assign('db_version', $this->dbver);
1141 $this->tmpl->show("credits.tpl");
1146 * create_thumbnails for the requested width
1148 * this function creates image thumbnails of $orig_image
1149 * stored as $thumb_image. It will check if the image is
1150 * in a supported format, if necessary rotate the image
1151 * (based on EXIF orientation meta headers) and re-sizing.
1153 public function create_thumbnail($orig_image, $thumb_image, $width)
1155 if(!file_exists($orig_image)) {
1159 $details = getimagesize($orig_image);
1161 /* check if original photo is a support image type */
1162 if(!$this->checkifImageSupported($details['mime']))
1165 $meta = $this->get_meta_informations($orig_image);
1171 switch($meta['Orientation']) {
1172 case 1: /* top, left */
1173 /* nothing to do */ break;
1174 case 2: /* top, right */
1175 $rotate = 0; $flip_hori = true; break;
1176 case 3: /* bottom, left */
1177 $rotate = 180; break;
1178 case 4: /* bottom, right */
1179 $flip_vert = true; break;
1180 case 5: /* left side, top */
1181 $rotate = 90; $flip_vert = true; break;
1182 case 6: /* right side, top */
1183 $rotate = 90; break;
1184 case 7: /* left side, bottom */
1185 $rotate = 270; $flip_vert = true; break;
1186 case 8: /* right side, bottom */
1187 $rotate = 270; break;
1190 $src_img = @imagecreatefromjpeg($orig_image);
1193 print "Can't load image from ". $orig_image ."\n";
1197 /* grabs the height and width */
1198 $cur_width = imagesx($src_img);
1199 $cur_height = imagesy($src_img);
1201 // If requested width is more then the actual image width,
1202 // do not generate a thumbnail, instead safe the original
1203 // as thumbnail but with lower quality. But if the image
1204 // is to heigh too, then we still have to resize it.
1205 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1206 $result = imagejpeg($src_img, $thumb_image, 75);
1207 imagedestroy($src_img);
1211 // If the image will be rotate because EXIF orientation said so
1212 // 'virtually rotate' the image for further calculations
1213 if($rotate == 90 || $rotate == 270) {
1215 $cur_width = $cur_height;
1219 /* calculates aspect ratio */
1220 $aspect_ratio = $cur_height / $cur_width;
1223 if($aspect_ratio < 1) {
1225 $new_h = abs($new_w * $aspect_ratio);
1227 /* 'virtually' rotate the image and calculate it's ratio */
1228 $tmp_w = $cur_height;
1229 $tmp_h = $cur_width;
1230 /* now get the ratio from the 'rotated' image */
1231 $tmp_ratio = $tmp_h/$tmp_w;
1232 /* now calculate the new dimensions */
1234 $tmp_h = abs($tmp_w * $tmp_ratio);
1236 // now that we know, how high they photo should be, if it
1237 // gets rotated, use this high to scale the image
1239 $new_w = abs($new_h / $aspect_ratio);
1241 // If the image will be rotate because EXIF orientation said so
1242 // now 'virtually rotate' back the image for the image manipulation
1243 if($rotate == 90 || $rotate == 270) {
1250 /* creates new image of that size */
1251 $dst_img = imagecreatetruecolor($new_w, $new_h);
1253 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1255 /* copies resized portion of original image into new image */
1256 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1258 /* needs the image to be flipped horizontal? */
1260 $this->_debug("(FLIP)");
1261 $dst_img = $this->flipImage($dst_img, 'hori');
1263 /* needs the image to be flipped vertical? */
1265 $this->_debug("(FLIP)");
1266 $dst_img = $this->flipImage($dst_img, 'vert');
1270 $this->_debug("(ROTATE)");
1271 $dst_img = $this->rotateImage($dst_img, $rotate);
1274 /* write down new generated file */
1275 $result = imagejpeg($dst_img, $thumb_image, 75);
1277 /* free your mind */
1278 imagedestroy($dst_img);
1279 imagedestroy($src_img);
1281 if($result === false) {
1282 print "Can't write thumbnail ". $thumb_image ."\n";
1288 } // create_thumbnail()
1291 * return all exif meta data from the file
1293 public function get_meta_informations($file)
1295 return exif_read_data($file);
1297 } // get_meta_informations()
1300 * create phpfspot own sqlite database
1302 * this function creates phpfspots own sqlite database
1303 * if it does not exist yet. this own is used to store
1304 * some necessary informations (md5 sum's, ...).
1306 public function check_config_table()
1308 // if the config table doesn't exist yet, create it
1309 if(!$this->cfg_db->db_check_table_exists("images")) {
1310 $this->cfg_db->db_exec("
1311 CREATE TABLE images (
1312 img_idx int primary key,
1318 } // check_config_table
1321 * Generates a thumbnail from photo idx
1323 * This function will generate JPEG thumbnails from provided F-Spot photo
1326 * 1. Check if all thumbnail generations (width) are already in place and
1328 * 2. Check if the md5sum of the original file has changed
1329 * 3. Generate the thumbnails if needed
1331 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1335 $resolutions = Array(
1336 $this->cfg->thumb_width,
1337 $this->cfg->photo_width,
1338 $this->cfg->mini_width,
1341 /* get details from F-Spot's database */
1342 $details = $this->get_photo_details($idx);
1344 /* calculate file MD5 sum */
1345 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1347 if(!file_exists($full_path)) {
1348 $this->_error("File ". $full_path ." does not exist\n");
1352 if(!is_readable($full_path)) {
1353 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1357 $file_md5 = md5_file($full_path);
1359 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1363 foreach($resolutions as $resolution) {
1365 $generate_it = false;
1367 $thumb_sub_path = substr($file_md5, 0, 2);
1368 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1370 if(!file_exists(dirname($thumb_path))) {
1371 mkdir(dirname($thumb_path), 0755);
1374 /* if the thumbnail file doesn't exist, create it */
1375 if(!file_exists($thumb_path)) {
1376 $generate_it = true;
1378 /* if the file hasn't changed there is no need to regen the thumb */
1379 elseif($file_md5 != $this->getMD5($idx) || $force) {
1380 $generate_it = true;
1383 if($generate_it || $overwrite) {
1385 $this->_debug(" ". $resolution ."px");
1386 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1394 $this->_debug(" already exist");
1397 /* set the new/changed MD5 sum for the current photo */
1399 $this->setMD5($idx, $file_md5);
1402 $this->_debug("\n");
1407 * returns stored md5 sum for a specific photo
1409 * this function queries the phpfspot database for a
1410 * stored MD5 checksum of the specified photo
1412 public function getMD5($idx)
1414 $result = $this->cfg_db->db_query("
1417 WHERE img_idx='". $idx ."'
1423 $img = $this->cfg_db->db_fetch_object($result);
1424 return $img['img_md5'];
1429 * set MD5 sum for the specific photo
1431 private function setMD5($idx, $md5)
1433 $result = $this->cfg_db->db_exec("
1434 REPLACE INTO images (img_idx, img_md5)
1435 VALUES ('". $idx ."', '". $md5 ."')
1441 * store current tag condition
1443 * this function stores the current tag condition
1444 * (AND or OR) in the users session variables
1446 public function setTagCondition($mode)
1448 $_SESSION['tag_condition'] = $mode;
1450 } // setTagCondition()
1453 * invoke tag & date search
1455 * this function will return all matching tags and store
1456 * them in the session variable selected_tags. furthermore
1457 * it also handles the date search.
1458 * getPhotoSelection() will then only return the matching
1461 public function startSearch($searchfor_tag, $from = 0, $to = 0)
1463 if(isset($_GET['from']) && $fspot->isValidDate($_GET['from'])) {
1464 $from = $_GET['from'];
1466 if(isset($_GET['to']) && $fspot->isValidDate($_GET['to'])) {
1470 if(isset($_GET['for_tag']) && is_string($_GET['for_tag'])) {
1471 $searchfor_tag = $_GET['for_tag'];
1474 if(isset($_GET['for_name']) && is_string($_GET['for_name'])) {
1475 $searchfor_name = $_GET['for_name'];
1480 $_SESSION['searchfor_tag'] = $searchfor_tag;
1481 $_SESSION['searchfor_name'] = $searchfor_name;
1484 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1486 unset($_SESSION['from_date']);
1489 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1491 unset($_SESSION['to_date']);
1493 if($searchfor_tag != "") {
1494 /* new search, reset the current selected tags */
1495 $_SESSION['selected_tags'] = Array();
1496 foreach($this->avail_tags as $tag) {
1497 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1498 array_push($_SESSION['selected_tags'], $tag);
1505 * updates sort order in session variable
1507 * this function is invoked by RPC and will sort the requested
1508 * sort order in the session variable.
1510 public function updateSortOrder($order)
1512 if(isset($this->sort_orders[$order])) {
1513 $_SESSION['sort_order'] = $order;
1517 return "unkown error";
1519 } // updateSortOrder()
1524 * this function rotates the image according the
1527 private function rotateImage($img, $degrees)
1529 if(function_exists("imagerotate")) {
1530 $img = imagerotate($img, $degrees, 0);
1532 function imagerotate($src_img, $angle)
1534 $src_x = imagesx($src_img);
1535 $src_y = imagesy($src_img);
1536 if ($angle == 180) {
1540 elseif ($src_x <= $src_y) {
1544 elseif ($src_x >= $src_y) {
1549 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1550 imagealphablending($rotate, false);
1555 for ($y = 0; $y < ($src_y); $y++) {
1556 for ($x = 0; $x < ($src_x); $x++) {
1557 $color = imagecolorat($src_img, $x, $y);
1558 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1564 for ($y = 0; $y < ($src_y); $y++) {
1565 for ($x = 0; $x < ($src_x); $x++) {
1566 $color = imagecolorat($src_img, $x, $y);
1567 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1573 for ($y = 0; $y < ($src_y); $y++) {
1574 for ($x = 0; $x < ($src_x); $x++) {
1575 $color = imagecolorat($src_img, $x, $y);
1576 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1590 $img = imagerotate($img, $degrees);
1599 * returns flipped image
1601 * this function will return an either horizontal or
1602 * vertical flipped truecolor image.
1604 private function flipImage($image, $mode)
1606 $w = imagesx($image);
1607 $h = imagesy($image);
1608 $flipped = imagecreatetruecolor($w, $h);
1612 for ($y = 0; $y < $h; $y++) {
1613 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1617 for ($x = 0; $x < $w; $x++) {
1618 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1628 * return all assigned tags for the specified photo
1630 private function get_photo_tags($idx)
1632 $result = $this->db->db_query("
1635 INNER JOIN photo_tags pt
1637 WHERE pt.photo_id='". $idx ."'
1642 while($row = $this->db->db_fetch_object($result))
1643 $tags[$row['id']] = $row['name'];
1647 } // get_photo_tags()
1650 * create on-the-fly images with text within
1652 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1654 if (strlen($color) != 6)
1657 $int = hexdec($color);
1658 $h = imagefontheight($font);
1659 $fw = imagefontwidth($font);
1660 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1661 $lines = count($txt);
1662 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1663 $bg = imagecolorallocate($im, 255, 255, 255);
1664 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1667 foreach ($txt as $text) {
1668 $x = (($w - ($fw * strlen($text))) / 2);
1669 imagestring($im, $font, $x, $y, $text, $color);
1670 $y += ($h + $space);
1673 Header("Content-type: image/png");
1676 } // showTextImage()
1679 * check if all requirements are met
1681 private function checkRequirements()
1683 if(!function_exists("imagecreatefromjpeg")) {
1684 print "PHP GD library extension is missing<br />\n";
1688 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1689 print "PHP SQLite3 library extension is missing<br />\n";
1693 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1694 ini_set('track_errors', 1);
1695 @include_once 'HTML/AJAX/Server.php';
1696 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1697 print "PEAR HTML_AJAX package is missing<br />\n";
1700 @include_once 'Calendar/Calendar.php';
1701 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1702 print "PEAR Calendar package is missing<br />\n";
1705 @include_once 'Console/Getopt.php';
1706 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1707 print "PEAR Console_Getopt package is missing<br />\n";
1710 ini_restore('track_errors');
1717 } // checkRequirements()
1719 private function _debug($text)
1721 if($this->fromcmd) {
1728 * check if specified MIME type is supported
1730 public function checkifImageSupported($mime)
1732 if(in_array($mime, Array("image/jpeg")))
1737 } // checkifImageSupported()
1739 public function _error($text)
1741 switch($this->cfg->logging) {
1744 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1745 print $text ."<br />\n";
1751 error_log($text, 3, $his->cfg->log_file);
1755 $this->runtime_error = true;
1760 * output calendard input fields
1762 private function get_calendar($mode)
1764 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1765 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1766 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1768 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1769 if(!isset($_SESSION[$mode .'_date']))
1770 $output.= " disabled=\"disabled\"";
1772 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1773 if(!isset($_SESSION[$mode .'_date']))
1774 $output.= " disabled=\"disabled\"";
1776 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1777 if(!isset($_SESSION[$mode .'_date']))
1778 $output.= " disabled=\"disabled\"";
1786 * output calendar matrix
1788 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1790 if (!isset($year)) $year = date('Y');
1791 if (!isset($month)) $month = date('m');
1792 if (!isset($day)) $day = date('d');
1797 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1798 require_once CALENDAR_ROOT.'Day.php';
1801 $month = new Calendar_Month_Weekdays($year,$month);
1804 $prevStamp = $month->prevMonth(true);
1805 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1806 $nextStamp = $month->nextMonth(true);
1807 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1809 $selectedDays = array (
1810 new Calendar_Day($year,$month,$day),
1811 new Calendar_Day($year,12,25),
1814 // Build the days in the month
1815 $month->build($selectedDays);
1817 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1818 $this->tmpl->assign('prev_month', $prev);
1819 $this->tmpl->assign('next_month', $next);
1821 while ( $day = $month->fetch() ) {
1823 if(!isset($matrix[$rows]))
1824 $matrix[$rows] = Array();
1828 $dayStamp = $day->thisDay(true);
1829 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1831 // isFirst() to find start of week
1832 if ( $day->isFirst() )
1835 if ( $day->isSelected() ) {
1836 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1837 } else if ( $day->isEmpty() ) {
1838 $string.= "<td> </td>\n";
1840 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1843 // isLast() to find end of week
1844 if ( $day->isLast() )
1845 $string.= "</tr>\n";
1847 $matrix[$rows][$cols] = $string;
1857 $this->tmpl->assign('matrix', $matrix);
1858 $this->tmpl->assign('rows', $rows);
1859 $this->tmpl->show("calendar.tpl");
1861 } // get_calendar_matrix()
1864 * output export page
1866 public function getExport($mode)
1868 $pictures = $this->getPhotoSelection();
1869 $current_tags = $this->getCurrentTags();
1871 foreach($pictures as $picture) {
1873 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1874 if($current_tags != "") {
1875 $orig_url.= "&tags=". $current_tags;
1877 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1878 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1881 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1886 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1887 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1891 // "[%pictureurl% %thumbnailurl%]"
1892 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1895 case 'MoinMoinList':
1896 // " * [%pictureurl% %thumbnailurl%]"
1897 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1908 public function getRSSFeed()
1910 Header("Content-type: text/xml; charset=utf-8");
1911 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1914 xmlns:media="http://search.yahoo.com/mrss/"
1915 xmlns:dc="http://purl.org/dc/elements/1.1/"
1918 <title>phpfspot</title>
1919 <description>phpfspot RSS feed</description>
1920 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1921 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1922 <generator>phpfspot</generator>
1925 $pictures = $this->getPhotoSelection();
1926 $current_tags = $this->getCurrentTags();
1928 foreach($pictures as $picture) {
1930 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1931 if($current_tags != "") {
1932 $orig_url.= "&tags=". $current_tags;
1934 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1935 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1938 $details = $this->get_photo_details($picture);
1940 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1941 $thumb_html = htmlspecialchars("
1942 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1944 ". $details['description']);
1946 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1947 $meta = $this->get_meta_informations($orig_path);
1948 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1952 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1953 <link><?php print htmlspecialchars($orig_url); ?></link>
1954 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1955 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1957 <?php print $thumb_html; ?>
1959 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1974 * return all selected tags as one string
1976 private function getCurrentTags()
1979 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1980 foreach($_SESSION['selected_tags'] as $tag)
1981 $current_tags.= $tag .",";
1982 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1984 return $current_tags;
1986 } // getCurrentTags()
1989 * return the current photo
1991 public function getCurrentPhoto()
1993 if(isset($_SESSION['current_photo'])) {
1994 print $_SESSION['current_photo'];
1996 } // getCurrentPhoto()
1999 * tells the client browser what to do
2001 * this function is getting called via AJAX by the
2002 * client browsers. it will tell them what they have
2003 * to do next. This is necessary for directly jumping
2004 * into photo index or single photo view when the are
2005 * requested with specific URLs
2007 public function whatToDo()
2009 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2010 return "show_photo";
2012 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2013 return "showpi_tags";
2015 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2019 return "nothing special";
2024 * return the current process-user
2026 private function getuid()
2028 if($uid = posix_getuid()) {
2029 if($user = posix_getpwuid($uid)) {
2030 return $user['name'];
2039 * returns a select-dropdown box to select photo index sort parameters
2041 public function smarty_sort_select_list($params, &$smarty)
2045 foreach($this->sort_orders as $key => $value) {
2046 $output.= "<option value=\"". $key ."\"";
2047 if($key == $_SESSION['sort_order']) {
2048 $output.= " selected=\"selected\"";
2050 $output.= ">". $value ."</option>";
2055 } // smarty_sort_select_list()
2058 * returns the currently selected sort order
2060 private function get_sort_order()
2062 switch($_SESSION['sort_order']) {
2064 return " ORDER BY p.time ASC";
2067 return " ORDER BY p.time DESC";
2070 if($this->dbver < 9) {
2071 return " ORDER BY p.name ASC";
2074 return " ORDER BY basename(p.uri) ASC";
2078 if($this->dbver < 9) {
2079 return " ORDER BY p.name DESC";
2082 return " ORDER BY basename(p.uri) DESC";
2086 return " ORDER BY t.name ASC ,p.time ASC";
2089 return " ORDER BY t.name DESC ,p.time ASC";
2093 } // get_sort_order()
2096 * return the next to be shown slide show image
2098 * this function returns the URL of the next image
2099 * in the slideshow sequence.
2101 public function getNextSlideShowImage()
2103 $all_photos = $this->getPhotoSelection();
2105 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2106 $_SESSION['slideshow_img'] = 0;
2108 $_SESSION['slideshow_img']++;
2110 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2112 } // getNextSlideShowImage()
2115 * return the previous to be shown slide show image
2117 * this function returns the URL of the previous image
2118 * in the slideshow sequence.
2120 public function getPrevSlideShowImage()
2122 $all_photos = $this->getPhotoSelection();
2124 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2125 $_SESSION['slideshow_img'] = 0;
2127 $_SESSION['slideshow_img']--;
2129 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2131 } // getPrevSlideShowImage()
2133 public function resetSlideShow()
2135 if(isset($_SESSION['slideshow_img']))
2136 unset($_SESSION['slideshow_img']);
2138 } // resetSlideShow()
2143 * this function will get all photos from the fspot
2144 * database and randomly return ONE entry
2146 * saddly there is yet no sqlite3 function which returns
2147 * the bulk result in array, so we have to fill up our
2150 public function get_random_photo()
2154 $result = $this->db->db_query("
2159 while($row = $this->db->db_fetch_object($result)) {
2160 array_push($all, $row['id']);
2163 return $all[array_rand($all)];
2165 } // get_random_photo()
2168 * validates provided date
2170 * this function validates if the provided date
2171 * contains a valid date and will return true
2174 public function isValidDate($date_str)
2176 $timestamp = strtotime($date_str);
2178 if(is_numeric($timestamp))
2186 * timestamp to string conversion
2188 private function ts2str($timestamp)
2190 return strftime("%Y-%m-%d", $timestamp);
2193 private function extractTags($tags_str)
2195 $not_validated = split(',', $_GET['tags']);
2196 $validated = array();
2198 foreach($not_validated as $tag) {
2199 if(is_numeric($tag))
2200 array_push($validated, $tag);
2208 * returns the full path to a thumbnail
2210 public function get_thumb_path($width, $photo)
2212 $md5 = $this->getMD5($photo);
2213 $sub_path = substr($md5, 0, 2);
2214 return $this->cfg->thumb_path
2222 } // get_thumb_path()
2225 * returns server's virtual host name
2227 private function get_server_name()
2229 return $_SERVER['SERVER_NAME'];
2230 } // get_server_name()
2233 * returns type of webprotocol which is
2236 private function get_web_protocol()
2238 if(!isset($_SERVER['HTTPS']))
2242 } // get_web_protocol()
2245 * return url to this phpfspot installation
2247 private function get_phpfspot_url()
2249 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2250 } // get_phpfspot_url()
2253 * check file exists and is readable
2255 * returns true, if everything is ok, otherwise false
2256 * if $silent is not set, this function will output and
2259 private function check_readable($file, $silent = null)
2261 if(!file_exists($file)) {
2263 print "File \"". $file ."\" does not exist.\n";
2267 if(!is_readable($file)) {
2269 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2275 } // check_readable()
2278 * check if all needed indices are present
2280 * this function checks, if some needed indices are already
2281 * present, or if not, create them on the fly. they are
2282 * necessary to speed up some queries like that one look for
2283 * all tags, when show_tags is specified in the configuration.
2285 private function checkDbIndices()
2287 $result = $this->db->db_exec("
2288 CREATE INDEX IF NOT EXISTS
2295 } // checkDbIndices()
2298 * retrive F-Spot database version
2300 * this function will return the F-Spot database version number
2301 * It is stored within the sqlite3 database in the table meta
2303 public function getFspotDBVersion()
2305 if($result = $this->db->db_fetchSingleRow("
2306 SELECT data as version
2309 name LIKE 'F-Spot Database Version'
2311 return $result['version'];
2315 } // getFspotDBVersion()
2318 * parse the provided URI and will returned the
2321 public function parse_uri($uri, $mode)
2323 if(($components = parse_url($uri)) !== false) {
2327 return basename($components['path']);
2330 return dirname($components['path']);
2333 return $components['path'];
2343 * validate config options
2345 * this function checks if all necessary configuration options are
2346 * specified and set.
2348 private function check_config_options()
2350 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2351 $this->_error("Please set \$page_title in phpfspot_cfg");
2353 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2354 $this->_error("Please set \$base_path in phpfspot_cfg");
2356 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2357 $this->_error("Please set \$web_path in phpfspot_cfg");
2359 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2360 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2362 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2363 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2365 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2366 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2368 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2369 $this->_error("Please set \$db_access in phpfspot_cfg");
2371 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2372 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2374 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2375 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2377 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2378 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2380 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2381 $this->_error("Please set \$photo_width in phpfspot_cfg");
2383 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2384 $this->_error("Please set \$mini_width in phpfspot_cfg");
2386 if(!isset($this->cfg->thumbs_per_page))
2387 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2389 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2390 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2392 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2393 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2395 if(!isset($this->cfg->hide_tags))
2396 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2398 if(!isset($this->cfg->theme_name))
2399 $this->_error("Please set \$theme_name in phpfspot_cfg");
2401 if(!isset($this->cfg->logging))
2402 $this->_error("Please set \$logging in phpfspot_cfg");
2404 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2406 if(!isset($this->cfg->log_file))
2407 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2409 if(!is_writeable($this->cfg->log_file))
2410 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2414 /* check for pending slash on web_path */
2415 if(!preg_match("/\/$/", $this->web_path))
2416 $this->web_path.= "/";
2418 return $this->runtime_error;
2420 } // check_config_options()
2423 * cleanup phpfspot own database
2425 * When photos are getting delete from F-Spot, there will remain
2426 * remain some residues in phpfspot own database. This function
2427 * will try to wipe them out.
2429 public function cleanup_phpfspot_db()
2431 $to_delete = Array();
2433 $result = $this->cfg_db->db_query("
2436 ORDER BY img_idx ASC
2439 while($row = $this->cfg_db->db_fetch_object($result)) {
2440 if(!$this->db->db_fetchSingleRow("
2443 WHERE id='". $row['img_idx'] ."'")) {
2445 array_push($to_delete, $row['img_idx'], ',');
2449 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2451 $this->cfg_db->db_exec("
2453 WHERE img_idx IN (". implode($to_delete) .")
2456 } // cleanup_phpfspot_db()