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_desc';
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_page', $this->getCurrentPage($current, $count));
507 $this->tmpl->assign('current_img', $photo);
510 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
511 $this->tmpl->assign('prev_img', $previous_img);
515 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
516 $this->tmpl->assign('next_img', $next_img);
518 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
519 $this->tmpl->assign('photo_number', $i);
520 $this->tmpl->assign('photo_count', count($all_photos));
522 $this->tmpl->show("single_photo.tpl");
527 * all available tags and tag cloud
529 * this function outputs all available tags (time ordered)
530 * and in addition output them as tag cloud (tags which have
531 * many photos will appears more then others)
533 public function getAvailableTags()
535 /* retrive tags from database */
540 $result = $this->db->db_query("
541 SELECT tag_id as id, count(tag_id) as quantity
551 while($row = $this->db->db_fetch_object($result)) {
552 $tags[$row['id']] = $row['quantity'];
555 // change these font sizes if you will
556 $max_size = 125; // max font size in %
557 $min_size = 75; // min font size in %
559 // get the largest and smallest array values
560 $max_qty = max(array_values($tags));
561 $min_qty = min(array_values($tags));
563 // find the range of values
564 $spread = $max_qty - $min_qty;
565 if (0 == $spread) { // we don't want to divide by zero
569 // determine the font-size increment
570 // this is the increase per tag quantity (times used)
571 $step = ($max_size - $min_size)/($spread);
573 // loop through our tag array
574 foreach ($tags as $key => $value) {
576 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
579 // calculate CSS font-size
580 // find the $value in excess of $min_qty
581 // multiply by the font-size increment ($size)
582 // and add the $min_size set above
583 $size = $min_size + (($value - $min_qty) * $step);
584 // uncomment if you want sizes in whole %:
587 if(isset($this->tags[$key])) {
588 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%;\">". $this->tags[$key] ."</a>, ";
593 $output = substr($output, 0, strlen($output)-2);
596 } // getAvailableTags()
599 * output all selected tags
601 * this function output all tags which have been selected
602 * by the user. the selected tags are stored in the
603 * session-variable $_SESSION['selected_tags']
605 public function getSelectedTags()
607 /* retrive tags from database */
612 foreach($this->avail_tags as $tag)
614 // return all selected tags
615 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
616 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
621 $output = substr($output, 0, strlen($output)-2);
625 return "no tags selected";
628 } // getSelectedTags()
631 * add tag to users session variable
633 * this function will add the specified to users current
634 * tag selection. if a date search has been made before
635 * it will be now cleared
637 public function addTag($tag)
639 if(!isset($_SESSION['selected_tags']))
640 $_SESSION['selected_tags'] = Array();
642 if(isset($_SESSION['searchfor_tag']))
643 unset($_SESSION['searchfor_tag']);
645 if(!in_array($tag, $_SESSION['selected_tags']))
646 array_push($_SESSION['selected_tags'], $tag);
654 * remove tag to users session variable
656 * this function removes the specified tag from
657 * users current tag selection
659 public function delTag($tag)
661 if(isset($_SESSION['searchfor_tag']))
662 unset($_SESSION['searchfor_tag']);
664 if(isset($_SESSION['selected_tags'])) {
665 $key = array_search($tag, $_SESSION['selected_tags']);
666 unset($_SESSION['selected_tags'][$key]);
667 sort($_SESSION['selected_tags']);
675 * reset tag selection
677 * if there is any tag selection, it will be
680 public function resetTags()
682 if(isset($_SESSION['selected_tags']))
683 unset($_SESSION['selected_tags']);
690 * if a specific photo was requested (external link)
691 * unset the session variable now
693 public function resetPhotoView()
695 if(isset($_SESSION['current_photo']))
696 unset($_SESSION['current_photo']);
698 } // resetPhotoView();
703 * if any tag search has taken place, reset it now
705 public function resetTagSearch()
707 if(isset($_SESSION['searchfor_tag']))
708 unset($_SESSION['searchfor_tag']);
710 } // resetTagSearch()
715 * if any name search has taken place, reset it now
717 public function resetNameSearch()
719 if(isset($_SESSION['searchfor_name']))
720 unset($_SESSION['searchfor_name']);
722 } // resetNameSearch()
727 * if any date search has taken place, reset
730 public function resetDateSearch()
732 if(isset($_SESSION['from_date']))
733 unset($_SESSION['from_date']);
734 if(isset($_SESSION['to_date']))
735 unset($_SESSION['to_date']);
737 } // resetDateSearch();
740 * return all photo according selection
742 * this function returns all photos based on
743 * the tag-selection, tag- or date-search.
744 * the tag-search also has to take care of AND
745 * and OR conjunctions
747 public function getPhotoSelection()
749 $matched_photos = Array();
750 $additional_where_cond = "";
752 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
753 $from_date = $_SESSION['from_date'];
754 $to_date = $_SESSION['to_date'];
755 $additional_where_cond.= "
756 p.time>='". $from_date ."'
758 p.time<='". $to_date ."'
762 if(isset($_SESSION['searchfor_name'])) {
763 if($this->dbver < 9) {
764 $additional_where_cond.= "
766 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
768 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
773 $additional_where_cond.= "
775 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
777 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
783 if(isset($_SESSION['sort_order'])) {
784 $order_str = $this->get_sort_order();
787 /* return a search result */
788 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
790 SELECT DISTINCT pt1.photo_id
792 INNER JOIN photo_tags pt2
793 ON pt1.photo_id=pt2.photo_id
800 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
802 if(isset($additional_where_cond) && !empty($additional_where_cond))
803 $query_str.= "AND ". $additional_where_cond ." ";
805 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
806 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
809 if(isset($order_str))
810 $query_str.= $order_str;
812 $result = $this->db->db_query($query_str);
813 while($row = $this->db->db_fetch_object($result)) {
814 array_push($matched_photos, $row['photo_id']);
816 return $matched_photos;
819 /* return according the selected tags */
820 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
822 foreach($_SESSION['selected_tags'] as $tag)
823 $selected.= $tag .",";
824 $selected = substr($selected, 0, strlen($selected)-1);
826 /* photo has to match at least on of the selected tags */
827 if($_SESSION['tag_condition'] == 'or') {
829 SELECT DISTINCT pt1.photo_id
831 INNER JOIN photo_tags pt2
832 ON pt1.photo_id=pt2.photo_id
837 WHERE pt1.tag_id IN (". $selected .")
839 if(isset($additional_where_cond) && !empty($additional_where_cond))
840 $query_str.= "AND ". $additional_where_cond ." ";
842 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
843 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
846 if(isset($order_str))
847 $query_str.= $order_str;
849 /* photo has to match all selected tags */
850 elseif($_SESSION['tag_condition'] == 'and') {
852 if(count($_SESSION['selected_tags']) >= 32) {
853 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
854 print "evaluate your tag selection. Please remove some tags from your selection.\n";
858 /* Join together a table looking like
860 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
862 so the query can quickly return all images matching the
863 selected tags in an AND condition
868 SELECT DISTINCT pt1.photo_id
872 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
879 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
881 INNER JOIN photo_tags pt". ($i+2) ."
882 ON pt1.photo_id=pt". ($i+2) .".photo_id
889 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
890 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
892 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
895 if(isset($additional_where_cond) && !empty($additional_where_cond))
896 $query_str.= "AND ". $additional_where_cond;
898 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
899 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
902 if(isset($order_str))
903 $query_str.= $order_str;
907 $result = $this->db->db_query($query_str);
908 while($row = $this->db->db_fetch_object($result)) {
909 array_push($matched_photos, $row['photo_id']);
911 return $matched_photos;
914 /* return all available photos */
918 LEFT JOIN photo_tags pt
924 if(isset($additional_where_cond) && !empty($additional_where_cond))
925 $query_str.= "WHERE ". $additional_where_cond ." ";
927 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
928 if(isset($additional_where_cond) && !empty($additional_where_cond))
929 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
931 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
934 if(isset($order_str))
935 $query_str.= $order_str;
937 $result = $this->db->db_query($query_str);
938 while($row = $this->db->db_fetch_object($result)) {
939 array_push($matched_photos, $row['id']);
941 return $matched_photos;
943 } // getPhotoSelection()
946 * control HTML ouput for photo index
948 * this function provides all the necessary information
949 * for the photo index template.
951 public function showPhotoIndex()
953 $photos = $this->getPhotoSelection();
955 $count = count($photos);
957 /* if all thumbnails should be shown on one page */
958 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
962 /* thumbnails should be splitted up in several pages */
963 elseif($this->cfg->thumbs_per_page > 0) {
965 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
969 $begin_with = $_SESSION['begin_with'];
972 $end_with = $begin_with + $this->cfg->thumbs_per_page;
976 $images[$thumbs] = Array();
977 $img_height[$thumbs] = Array();
978 $img_width[$thumbs] = Array();
979 $img_id[$thumbs] = Array();
980 $img_name[$thumbs] = Array();
981 $img_title = Array();
983 for($i = $begin_with; $i < $end_with; $i++) {
985 if(isset($photos[$i])) {
987 $images[$thumbs] = $photos[$i];
988 $img_id[$thumbs] = $i;
989 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
990 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
992 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
994 if(file_exists($thumb_path)) {
995 $info = getimagesize($thumb_path);
996 $img_width[$thumbs] = $info[0];
997 $img_height[$thumbs] = $info[1];
1003 // +1 for for smarty's selection iteration
1006 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1007 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1009 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1010 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1011 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1014 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1015 $this->tmpl->assign('tag_result', 1);
1018 /* do we have to display the page selector ? */
1019 if($this->cfg->thumbs_per_page != 0) {
1023 /* calculate the page switchers */
1024 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1025 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1027 if($begin_with != 0)
1028 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1029 if($end_with < $count)
1030 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1032 $photo_per_page = $this->cfg->thumbs_per_page;
1033 $last_page = ceil($count / $photo_per_page);
1035 /* get the current selected page */
1036 if($begin_with == 0) {
1040 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1047 for($i = 1; $i <= $last_page; $i++) {
1049 if($current_page == $i)
1050 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1051 elseif($current_page-1 == $i || $current_page+1 == $i)
1052 $style = "style=\"font-size: 105%;\"";
1053 elseif(($current_page-5 >= $i) && ($i != 1) ||
1054 ($current_page+5 <= $i) && ($i != $last_page))
1055 $style = "style=\"font-size: 75%;\"";
1059 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1062 $select.= ">". $i ."</a> ";
1064 // until 9 pages we show the selector from 1-9
1065 if($last_page <= 9) {
1066 $page_select.= $select;
1069 if($i == 1 /* first page */ ||
1070 $i == $last_page /* last page */ ||
1071 $i == $current_page /* current page */ ||
1072 $i == ceil($last_page * 0.25) /* first quater */ ||
1073 $i == ceil($last_page * 0.5) /* half */ ||
1074 $i == ceil($last_page * 0.75) /* third quater */ ||
1075 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1076 (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 */ ||
1077 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1078 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1080 $page_select.= $select;
1088 $page_select.= "......... ";
1093 /* only show the page selector if we have more then one page */
1095 $this->tmpl->assign('page_selector', $page_select);
1099 $current_tags = $this->getCurrentTags();
1100 $extern_link = "index.php?mode=showpi";
1101 $rss_link = "index.php?mode=rss";
1102 if($current_tags != "") {
1103 $extern_link.= "&tags=". $current_tags;
1104 $rss_link.= "&tags=". $current_tags;
1106 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1107 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1108 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1111 $export_link = "index.php?mode=export";
1112 $slideshow_link = "index.php?mode=slideshow";
1114 $this->tmpl->assign('extern_link', $extern_link);
1115 $this->tmpl->assign('slideshow_link', $slideshow_link);
1116 $this->tmpl->assign('export_link', $export_link);
1117 $this->tmpl->assign('rss_link', $rss_link);
1118 $this->tmpl->assign('count', $count);
1119 $this->tmpl->assign('width', $this->cfg->thumb_width);
1120 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1121 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1122 $this->tmpl->assign('images', $images);
1123 $this->tmpl->assign('img_width', $img_width);
1124 $this->tmpl->assign('img_height', $img_height);
1125 $this->tmpl->assign('img_id', $img_id);
1126 $this->tmpl->assign('img_name', $img_name);
1127 $this->tmpl->assign('img_title', $img_title);
1128 $this->tmpl->assign('thumbs', $thumbs);
1130 $this->tmpl->show("photo_index.tpl");
1132 /* if we are returning to photo index from an photo-view,
1133 scroll the window to the last shown photo-thumbnail.
1134 after this, unset the last_photo session variable.
1136 if(isset($_SESSION['last_photo'])) {
1137 print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1138 unset($_SESSION['last_photo']);
1141 } // showPhotoIndex()
1144 * show credit template
1146 public function showCredits()
1148 $this->tmpl->assign('version', $this->cfg->version);
1149 $this->tmpl->assign('product', $this->cfg->product);
1150 $this->tmpl->assign('db_version', $this->dbver);
1151 $this->tmpl->show("credits.tpl");
1156 * create_thumbnails for the requested width
1158 * this function creates image thumbnails of $orig_image
1159 * stored as $thumb_image. It will check if the image is
1160 * in a supported format, if necessary rotate the image
1161 * (based on EXIF orientation meta headers) and re-sizing.
1163 public function create_thumbnail($orig_image, $thumb_image, $width)
1165 if(!file_exists($orig_image)) {
1169 $details = getimagesize($orig_image);
1171 /* check if original photo is a support image type */
1172 if(!$this->checkifImageSupported($details['mime']))
1175 $meta = $this->get_meta_informations($orig_image);
1181 switch($meta['Orientation']) {
1182 case 1: /* top, left */
1183 /* nothing to do */ break;
1184 case 2: /* top, right */
1185 $rotate = 0; $flip_hori = true; break;
1186 case 3: /* bottom, left */
1187 $rotate = 180; break;
1188 case 4: /* bottom, right */
1189 $flip_vert = true; break;
1190 case 5: /* left side, top */
1191 $rotate = 90; $flip_vert = true; break;
1192 case 6: /* right side, top */
1193 $rotate = 90; break;
1194 case 7: /* left side, bottom */
1195 $rotate = 270; $flip_vert = true; break;
1196 case 8: /* right side, bottom */
1197 $rotate = 270; break;
1200 $src_img = @imagecreatefromjpeg($orig_image);
1203 print "Can't load image from ". $orig_image ."\n";
1207 /* grabs the height and width */
1208 $cur_width = imagesx($src_img);
1209 $cur_height = imagesy($src_img);
1211 // If requested width is more then the actual image width,
1212 // do not generate a thumbnail, instead safe the original
1213 // as thumbnail but with lower quality. But if the image
1214 // is to heigh too, then we still have to resize it.
1215 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1216 $result = imagejpeg($src_img, $thumb_image, 75);
1217 imagedestroy($src_img);
1221 // If the image will be rotate because EXIF orientation said so
1222 // 'virtually rotate' the image for further calculations
1223 if($rotate == 90 || $rotate == 270) {
1225 $cur_width = $cur_height;
1229 /* calculates aspect ratio */
1230 $aspect_ratio = $cur_height / $cur_width;
1233 if($aspect_ratio < 1) {
1235 $new_h = abs($new_w * $aspect_ratio);
1237 /* 'virtually' rotate the image and calculate it's ratio */
1238 $tmp_w = $cur_height;
1239 $tmp_h = $cur_width;
1240 /* now get the ratio from the 'rotated' image */
1241 $tmp_ratio = $tmp_h/$tmp_w;
1242 /* now calculate the new dimensions */
1244 $tmp_h = abs($tmp_w * $tmp_ratio);
1246 // now that we know, how high they photo should be, if it
1247 // gets rotated, use this high to scale the image
1249 $new_w = abs($new_h / $aspect_ratio);
1251 // If the image will be rotate because EXIF orientation said so
1252 // now 'virtually rotate' back the image for the image manipulation
1253 if($rotate == 90 || $rotate == 270) {
1260 /* creates new image of that size */
1261 $dst_img = imagecreatetruecolor($new_w, $new_h);
1263 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1265 /* copies resized portion of original image into new image */
1266 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1268 /* needs the image to be flipped horizontal? */
1270 $this->_debug("(FLIP)");
1271 $dst_img = $this->flipImage($dst_img, 'hori');
1273 /* needs the image to be flipped vertical? */
1275 $this->_debug("(FLIP)");
1276 $dst_img = $this->flipImage($dst_img, 'vert');
1280 $this->_debug("(ROTATE)");
1281 $dst_img = $this->rotateImage($dst_img, $rotate);
1284 /* write down new generated file */
1285 $result = imagejpeg($dst_img, $thumb_image, 75);
1287 /* free your mind */
1288 imagedestroy($dst_img);
1289 imagedestroy($src_img);
1291 if($result === false) {
1292 print "Can't write thumbnail ". $thumb_image ."\n";
1298 } // create_thumbnail()
1301 * return all exif meta data from the file
1303 public function get_meta_informations($file)
1305 return exif_read_data($file);
1307 } // get_meta_informations()
1310 * create phpfspot own sqlite database
1312 * this function creates phpfspots own sqlite database
1313 * if it does not exist yet. this own is used to store
1314 * some necessary informations (md5 sum's, ...).
1316 public function check_config_table()
1318 // if the config table doesn't exist yet, create it
1319 if(!$this->cfg_db->db_check_table_exists("images")) {
1320 $this->cfg_db->db_exec("
1321 CREATE TABLE images (
1322 img_idx int primary key,
1328 } // check_config_table
1331 * Generates a thumbnail from photo idx
1333 * This function will generate JPEG thumbnails from provided F-Spot photo
1336 * 1. Check if all thumbnail generations (width) are already in place and
1338 * 2. Check if the md5sum of the original file has changed
1339 * 3. Generate the thumbnails if needed
1341 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1345 $resolutions = Array(
1346 $this->cfg->thumb_width,
1347 $this->cfg->photo_width,
1348 $this->cfg->mini_width,
1351 /* get details from F-Spot's database */
1352 $details = $this->get_photo_details($idx);
1354 /* calculate file MD5 sum */
1355 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1357 if(!file_exists($full_path)) {
1358 $this->_error("File ". $full_path ." does not exist\n");
1362 if(!is_readable($full_path)) {
1363 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1367 $file_md5 = md5_file($full_path);
1369 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1373 foreach($resolutions as $resolution) {
1375 $generate_it = false;
1377 $thumb_sub_path = substr($file_md5, 0, 2);
1378 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1380 if(!file_exists(dirname($thumb_path))) {
1381 mkdir(dirname($thumb_path), 0755);
1384 /* if the thumbnail file doesn't exist, create it */
1385 if(!file_exists($thumb_path)) {
1386 $generate_it = true;
1388 /* if the file hasn't changed there is no need to regen the thumb */
1389 elseif($file_md5 != $this->getMD5($idx) || $force) {
1390 $generate_it = true;
1393 if($generate_it || $overwrite) {
1395 $this->_debug(" ". $resolution ."px");
1396 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1404 $this->_debug(" already exist");
1407 /* set the new/changed MD5 sum for the current photo */
1409 $this->setMD5($idx, $file_md5);
1412 $this->_debug("\n");
1417 * returns stored md5 sum for a specific photo
1419 * this function queries the phpfspot database for a
1420 * stored MD5 checksum of the specified photo
1422 public function getMD5($idx)
1424 $result = $this->cfg_db->db_query("
1427 WHERE img_idx='". $idx ."'
1433 $img = $this->cfg_db->db_fetch_object($result);
1434 return $img['img_md5'];
1439 * set MD5 sum for the specific photo
1441 private function setMD5($idx, $md5)
1443 $result = $this->cfg_db->db_exec("
1444 REPLACE INTO images (img_idx, img_md5)
1445 VALUES ('". $idx ."', '". $md5 ."')
1451 * store current tag condition
1453 * this function stores the current tag condition
1454 * (AND or OR) in the users session variables
1456 public function setTagCondition($mode)
1458 $_SESSION['tag_condition'] = $mode;
1462 } // setTagCondition()
1465 * invoke tag & date search
1467 * this function will return all matching tags and store
1468 * them in the session variable selected_tags. furthermore
1469 * it also handles the date search.
1470 * getPhotoSelection() will then only return the matching
1473 public function startSearch()
1475 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1476 $from = $_POST['from'];
1478 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1482 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1483 $searchfor_tag = $_POST['for_tag'];
1484 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1487 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1488 $searchfor_name = $_POST['for_name'];
1489 $_SESSION['searchfor_name'] = $_POST['for_name'];
1494 if(isset($from) && !empty($from))
1495 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1497 unset($_SESSION['from_date']);
1499 if(isset($to) && !empty($to))
1500 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1502 unset($_SESSION['to_date']);
1504 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1505 /* new search, reset the current selected tags */
1506 $_SESSION['selected_tags'] = Array();
1507 foreach($this->avail_tags as $tag) {
1508 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1509 array_push($_SESSION['selected_tags'], $tag);
1518 * updates sort order in session variable
1520 * this function is invoked by RPC and will sort the requested
1521 * sort order in the session variable.
1523 public function updateSortOrder($order)
1525 if(isset($this->sort_orders[$order])) {
1526 $_SESSION['sort_order'] = $order;
1530 return "unkown error";
1532 } // updateSortOrder()
1537 * this function rotates the image according the
1540 private function rotateImage($img, $degrees)
1542 if(function_exists("imagerotate")) {
1543 $img = imagerotate($img, $degrees, 0);
1545 function imagerotate($src_img, $angle)
1547 $src_x = imagesx($src_img);
1548 $src_y = imagesy($src_img);
1549 if ($angle == 180) {
1553 elseif ($src_x <= $src_y) {
1557 elseif ($src_x >= $src_y) {
1562 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1563 imagealphablending($rotate, false);
1568 for ($y = 0; $y < ($src_y); $y++) {
1569 for ($x = 0; $x < ($src_x); $x++) {
1570 $color = imagecolorat($src_img, $x, $y);
1571 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1577 for ($y = 0; $y < ($src_y); $y++) {
1578 for ($x = 0; $x < ($src_x); $x++) {
1579 $color = imagecolorat($src_img, $x, $y);
1580 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1586 for ($y = 0; $y < ($src_y); $y++) {
1587 for ($x = 0; $x < ($src_x); $x++) {
1588 $color = imagecolorat($src_img, $x, $y);
1589 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1603 $img = imagerotate($img, $degrees);
1612 * returns flipped image
1614 * this function will return an either horizontal or
1615 * vertical flipped truecolor image.
1617 private function flipImage($image, $mode)
1619 $w = imagesx($image);
1620 $h = imagesy($image);
1621 $flipped = imagecreatetruecolor($w, $h);
1625 for ($y = 0; $y < $h; $y++) {
1626 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1630 for ($x = 0; $x < $w; $x++) {
1631 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1641 * return all assigned tags for the specified photo
1643 private function get_photo_tags($idx)
1645 $result = $this->db->db_query("
1648 INNER JOIN photo_tags pt
1650 WHERE pt.photo_id='". $idx ."'
1655 while($row = $this->db->db_fetch_object($result))
1656 $tags[$row['id']] = $row['name'];
1660 } // get_photo_tags()
1663 * create on-the-fly images with text within
1665 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1667 if (strlen($color) != 6)
1670 $int = hexdec($color);
1671 $h = imagefontheight($font);
1672 $fw = imagefontwidth($font);
1673 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1674 $lines = count($txt);
1675 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1676 $bg = imagecolorallocate($im, 255, 255, 255);
1677 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1680 foreach ($txt as $text) {
1681 $x = (($w - ($fw * strlen($text))) / 2);
1682 imagestring($im, $font, $x, $y, $text, $color);
1683 $y += ($h + $space);
1686 Header("Content-type: image/png");
1689 } // showTextImage()
1692 * check if all requirements are met
1694 private function checkRequirements()
1696 if(!function_exists("imagecreatefromjpeg")) {
1697 print "PHP GD library extension is missing<br />\n";
1701 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1702 print "PHP SQLite3 library extension is missing<br />\n";
1706 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1707 ini_set('track_errors', 1);
1708 @include_once 'HTML/AJAX/Server.php';
1709 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1710 print "PEAR HTML_AJAX package is missing<br />\n";
1713 @include_once 'Calendar/Calendar.php';
1714 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1715 print "PEAR Calendar package is missing<br />\n";
1718 @include_once 'Console/Getopt.php';
1719 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1720 print "PEAR Console_Getopt package is missing<br />\n";
1723 ini_restore('track_errors');
1730 } // checkRequirements()
1732 private function _debug($text)
1734 if($this->fromcmd) {
1741 * check if specified MIME type is supported
1743 public function checkifImageSupported($mime)
1745 if(in_array($mime, Array("image/jpeg")))
1750 } // checkifImageSupported()
1752 public function _error($text)
1754 switch($this->cfg->logging) {
1757 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1758 print $text ."<br />\n";
1764 error_log($text, 3, $his->cfg->log_file);
1768 $this->runtime_error = true;
1773 * output calendard input fields
1775 private function get_calendar($mode)
1777 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1778 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1779 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1781 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1782 if(!isset($_SESSION[$mode .'_date']))
1783 $output.= " disabled=\"disabled\"";
1785 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1786 if(!isset($_SESSION[$mode .'_date']))
1787 $output.= " disabled=\"disabled\"";
1789 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1790 if(!isset($_SESSION[$mode .'_date']))
1791 $output.= " disabled=\"disabled\"";
1799 * output calendar matrix
1801 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1803 if (!isset($year)) $year = date('Y');
1804 if (!isset($month)) $month = date('m');
1805 if (!isset($day)) $day = date('d');
1810 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1811 require_once CALENDAR_ROOT.'Day.php';
1814 $month = new Calendar_Month_Weekdays($year,$month);
1817 $prevStamp = $month->prevMonth(true);
1818 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1819 $nextStamp = $month->nextMonth(true);
1820 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1822 $selectedDays = array (
1823 new Calendar_Day($year,$month,$day),
1824 new Calendar_Day($year,12,25),
1827 // Build the days in the month
1828 $month->build($selectedDays);
1830 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1831 $this->tmpl->assign('prev_month', $prev);
1832 $this->tmpl->assign('next_month', $next);
1834 while ( $day = $month->fetch() ) {
1836 if(!isset($matrix[$rows]))
1837 $matrix[$rows] = Array();
1841 $dayStamp = $day->thisDay(true);
1842 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1844 // isFirst() to find start of week
1845 if ( $day->isFirst() )
1848 if ( $day->isSelected() ) {
1849 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1850 } else if ( $day->isEmpty() ) {
1851 $string.= "<td> </td>\n";
1853 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1856 // isLast() to find end of week
1857 if ( $day->isLast() )
1858 $string.= "</tr>\n";
1860 $matrix[$rows][$cols] = $string;
1870 $this->tmpl->assign('matrix', $matrix);
1871 $this->tmpl->assign('rows', $rows);
1872 $this->tmpl->show("calendar.tpl");
1874 } // get_calendar_matrix()
1877 * output export page
1879 public function getExport($mode)
1881 $pictures = $this->getPhotoSelection();
1882 $current_tags = $this->getCurrentTags();
1884 foreach($pictures as $picture) {
1886 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1887 if($current_tags != "") {
1888 $orig_url.= "&tags=". $current_tags;
1890 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1891 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1894 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1899 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
1900 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
1904 // "[%pictureurl% %thumbnailurl%]"
1905 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1908 case 'MoinMoinList':
1909 // " * [%pictureurl% %thumbnailurl%]"
1910 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
1921 public function getRSSFeed()
1923 Header("Content-type: text/xml; charset=utf-8");
1924 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1927 xmlns:media="http://search.yahoo.com/mrss/"
1928 xmlns:dc="http://purl.org/dc/elements/1.1/"
1931 <title>phpfspot</title>
1932 <description>phpfspot RSS feed</description>
1933 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
1934 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
1935 <generator>phpfspot</generator>
1938 $pictures = $this->getPhotoSelection();
1939 $current_tags = $this->getCurrentTags();
1941 foreach($pictures as $picture) {
1943 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
1944 if($current_tags != "") {
1945 $orig_url.= "&tags=". $current_tags;
1947 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1948 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
1951 $details = $this->get_photo_details($picture);
1953 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
1954 $thumb_html = htmlspecialchars("
1955 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
1957 ". $details['description']);
1959 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1960 $meta = $this->get_meta_informations($orig_path);
1961 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
1965 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
1966 <link><?php print htmlspecialchars($orig_url); ?></link>
1967 <guid><?php print htmlspecialchars($orig_url); ?></guid>
1968 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
1970 <?php print $thumb_html; ?>
1972 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
1987 * return all selected tags as one string
1989 private function getCurrentTags()
1992 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
1993 foreach($_SESSION['selected_tags'] as $tag)
1994 $current_tags.= $tag .",";
1995 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
1997 return $current_tags;
1999 } // getCurrentTags()
2002 * return the current photo
2004 public function getCurrentPhoto()
2006 if(isset($_SESSION['current_photo'])) {
2007 print $_SESSION['current_photo'];
2009 } // getCurrentPhoto()
2012 * tells the client browser what to do
2014 * this function is getting called via AJAX by the
2015 * client browsers. it will tell them what they have
2016 * to do next. This is necessary for directly jumping
2017 * into photo index or single photo view when the are
2018 * requested with specific URLs
2020 public function whatToDo()
2022 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2023 return "show_photo";
2025 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2026 return "showpi_tags";
2028 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2032 return "nothing special";
2037 * return the current process-user
2039 private function getuid()
2041 if($uid = posix_getuid()) {
2042 if($user = posix_getpwuid($uid)) {
2043 return $user['name'];
2052 * returns a select-dropdown box to select photo index sort parameters
2054 public function smarty_sort_select_list($params, &$smarty)
2058 foreach($this->sort_orders as $key => $value) {
2059 $output.= "<option value=\"". $key ."\"";
2060 if($key == $_SESSION['sort_order']) {
2061 $output.= " selected=\"selected\"";
2063 $output.= ">". $value ."</option>";
2068 } // smarty_sort_select_list()
2071 * returns the currently selected sort order
2073 private function get_sort_order()
2075 switch($_SESSION['sort_order']) {
2077 return " ORDER BY p.time ASC";
2080 return " ORDER BY p.time DESC";
2083 if($this->dbver < 9) {
2084 return " ORDER BY p.name ASC";
2087 return " ORDER BY basename(p.uri) ASC";
2091 if($this->dbver < 9) {
2092 return " ORDER BY p.name DESC";
2095 return " ORDER BY basename(p.uri) DESC";
2099 return " ORDER BY t.name ASC ,p.time ASC";
2102 return " ORDER BY t.name DESC ,p.time ASC";
2106 } // get_sort_order()
2109 * return the next to be shown slide show image
2111 * this function returns the URL of the next image
2112 * in the slideshow sequence.
2114 public function getNextSlideShowImage()
2116 $all_photos = $this->getPhotoSelection();
2118 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2119 $_SESSION['slideshow_img'] = 0;
2121 $_SESSION['slideshow_img']++;
2123 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2125 } // getNextSlideShowImage()
2128 * return the previous to be shown slide show image
2130 * this function returns the URL of the previous image
2131 * in the slideshow sequence.
2133 public function getPrevSlideShowImage()
2135 $all_photos = $this->getPhotoSelection();
2137 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2138 $_SESSION['slideshow_img'] = 0;
2140 $_SESSION['slideshow_img']--;
2142 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2144 } // getPrevSlideShowImage()
2146 public function resetSlideShow()
2148 if(isset($_SESSION['slideshow_img']))
2149 unset($_SESSION['slideshow_img']);
2151 } // resetSlideShow()
2156 * this function will get all photos from the fspot
2157 * database and randomly return ONE entry
2159 * saddly there is yet no sqlite3 function which returns
2160 * the bulk result in array, so we have to fill up our
2163 public function get_random_photo()
2167 $result = $this->db->db_query("
2172 while($row = $this->db->db_fetch_object($result)) {
2173 array_push($all, $row['id']);
2176 return $all[array_rand($all)];
2178 } // get_random_photo()
2181 * validates provided date
2183 * this function validates if the provided date
2184 * contains a valid date and will return true
2187 public function isValidDate($date_str)
2189 $timestamp = strtotime($date_str);
2191 if(is_numeric($timestamp))
2199 * timestamp to string conversion
2201 private function ts2str($timestamp)
2203 return strftime("%Y-%m-%d", $timestamp);
2206 private function extractTags($tags_str)
2208 $not_validated = split(',', $_GET['tags']);
2209 $validated = array();
2211 foreach($not_validated as $tag) {
2212 if(is_numeric($tag))
2213 array_push($validated, $tag);
2221 * returns the full path to a thumbnail
2223 public function get_thumb_path($width, $photo)
2225 $md5 = $this->getMD5($photo);
2226 $sub_path = substr($md5, 0, 2);
2227 return $this->cfg->thumb_path
2235 } // get_thumb_path()
2238 * returns server's virtual host name
2240 private function get_server_name()
2242 return $_SERVER['SERVER_NAME'];
2243 } // get_server_name()
2246 * returns type of webprotocol which is
2249 private function get_web_protocol()
2251 if(!isset($_SERVER['HTTPS']))
2255 } // get_web_protocol()
2258 * return url to this phpfspot installation
2260 private function get_phpfspot_url()
2262 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2263 } // get_phpfspot_url()
2266 * check file exists and is readable
2268 * returns true, if everything is ok, otherwise false
2269 * if $silent is not set, this function will output and
2272 private function check_readable($file, $silent = null)
2274 if(!file_exists($file)) {
2276 print "File \"". $file ."\" does not exist.\n";
2280 if(!is_readable($file)) {
2282 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2288 } // check_readable()
2291 * check if all needed indices are present
2293 * this function checks, if some needed indices are already
2294 * present, or if not, create them on the fly. they are
2295 * necessary to speed up some queries like that one look for
2296 * all tags, when show_tags is specified in the configuration.
2298 private function checkDbIndices()
2300 $result = $this->db->db_exec("
2301 CREATE INDEX IF NOT EXISTS
2308 } // checkDbIndices()
2311 * retrive F-Spot database version
2313 * this function will return the F-Spot database version number
2314 * It is stored within the sqlite3 database in the table meta
2316 public function getFspotDBVersion()
2318 if($result = $this->db->db_fetchSingleRow("
2319 SELECT data as version
2322 name LIKE 'F-Spot Database Version'
2324 return $result['version'];
2328 } // getFspotDBVersion()
2331 * parse the provided URI and will returned the
2334 public function parse_uri($uri, $mode)
2336 if(($components = parse_url($uri)) !== false) {
2340 return basename($components['path']);
2343 return dirname($components['path']);
2346 return $components['path'];
2356 * validate config options
2358 * this function checks if all necessary configuration options are
2359 * specified and set.
2361 private function check_config_options()
2363 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2364 $this->_error("Please set \$page_title in phpfspot_cfg");
2366 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2367 $this->_error("Please set \$base_path in phpfspot_cfg");
2369 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2370 $this->_error("Please set \$web_path in phpfspot_cfg");
2372 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2373 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2375 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2376 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2378 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2379 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2381 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2382 $this->_error("Please set \$db_access in phpfspot_cfg");
2384 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2385 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2387 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2388 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2390 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2391 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2393 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2394 $this->_error("Please set \$photo_width in phpfspot_cfg");
2396 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2397 $this->_error("Please set \$mini_width in phpfspot_cfg");
2399 if(!isset($this->cfg->thumbs_per_page))
2400 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2402 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2403 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2405 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2406 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2408 if(!isset($this->cfg->hide_tags))
2409 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2411 if(!isset($this->cfg->theme_name))
2412 $this->_error("Please set \$theme_name in phpfspot_cfg");
2414 if(!isset($this->cfg->logging))
2415 $this->_error("Please set \$logging in phpfspot_cfg");
2417 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2419 if(!isset($this->cfg->log_file))
2420 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2422 if(!is_writeable($this->cfg->log_file))
2423 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2427 /* check for pending slash on web_path */
2428 if(!preg_match("/\/$/", $this->cfg->web_path))
2429 $this->cfg->web_path.= "/";
2431 return $this->runtime_error;
2433 } // check_config_options()
2436 * cleanup phpfspot own database
2438 * When photos are getting delete from F-Spot, there will remain
2439 * remain some residues in phpfspot own database. This function
2440 * will try to wipe them out.
2442 public function cleanup_phpfspot_db()
2444 $to_delete = Array();
2446 $result = $this->cfg_db->db_query("
2449 ORDER BY img_idx ASC
2452 while($row = $this->cfg_db->db_fetch_object($result)) {
2453 if(!$this->db->db_fetchSingleRow("
2456 WHERE id='". $row['img_idx'] ."'")) {
2458 array_push($to_delete, $row['img_idx'], ',');
2462 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2464 $this->cfg_db->db_exec("
2466 WHERE img_idx IN (". implode($to_delete) .")
2469 } // cleanup_phpfspot_db()
2472 * return first image of the page, the $current photo
2475 * this function is used to find out the first photo of the
2476 * current page, in which the $current photo lies. this is
2477 * used to display the correct photo, when calling showPhotoIndex()
2480 private function getCurrentPage($current, $max)
2482 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
2483 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
2484 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
2490 } // getCurrentPage()