3 /***************************************************************************
5 * phpfspot, presents your F-Spot photo collection in Web browsers.
7 * Copyright (c) by Andreas Unterkircher
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 ***************************************************************************/
25 require_once "phpfspot_cfg.php";
26 require_once "phpfspot_db.php";
37 private $runtime_error = false;
43 * this function will be called on class construct
44 * and will check requirements, loads configuration,
45 * open databases and start the user session
47 public function __construct()
50 * register PHPFSPOT class global
52 * @global PHPFSPOT $GLOBALS['phpfspot']
55 $GLOBALS['phpfspot'] =& $this;
57 $this->cfg = new PHPFSPOT_CFG;
59 /* verify config settings */
60 if($this->check_config_options()) {
64 /* set application name and version information */
65 $this->cfg->product = "phpfspot";
66 $this->cfg->version = "1.4";
68 $this->sort_orders= array(
69 'date_asc' => 'Date ↑',
70 'date_desc' => 'Date ↓',
71 'name_asc' => 'Name ↑',
72 'name_desc' => 'Name ↓',
73 'tags_asc' => 'Tags ↑',
74 'tags_desc' => 'Tags ↓',
77 /* Check necessary requirements */
78 if(!$this->check_requirements()) {
82 /******* Opening F-Spot's sqlite database *********/
84 /* Check if database file is writeable */
85 if(!is_writeable($this->cfg->fspot_db)) {
86 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
90 /* open the database */
91 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
93 /* change sqlite temp directory, if requested */
94 if(isset($this->cfg->sqlite_temp_dir)) {
97 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
101 $this->dbver = $this->getFspotDBVersion();
103 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
104 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
108 if(!is_writeable($this->cfg->thumb_path)) {
109 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
113 /******* Opening phpfspot's sqlite database *********/
115 /* Check if directory where the database file is stored is writeable */
116 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
117 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
121 /* Check if database file is writeable */
122 if(!is_writeable($this->cfg->phpfspot_db)) {
123 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
127 /* open the database */
128 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
130 /* change sqlite temp directory, if requested */
131 if(isset($this->cfg->sqlite_temp_dir)) {
132 $this->cfg_db->db_exec("
134 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
138 /* Check if some tables need to be created */
139 $this->check_config_table();
141 /* overload Smarty class with our own template handler */
142 require_once "phpfspot_tmpl.php";
143 $this->tmpl = new PHPFSPOT_TMPL();
145 /* check if all necessary indices exist */
146 $this->checkDbIndices();
148 /* if session is not yet started, do it now */
149 if(session_id() == "")
152 if(!isset($_SESSION['tag_condition']))
153 $_SESSION['tag_condition'] = 'or';
155 if(!isset($_SESSION['sort_order']))
156 $_SESSION['sort_order'] = 'date_desc';
158 if(!isset($_SESSION['searchfor_tag']))
159 $_SESSION['searchfor_tag'] = '';
161 // if begin_with is still set but thumbs_per_page is now 0, unset it
162 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
163 unset($_SESSION['begin_with']);
167 public function __destruct()
173 * show - generate html output
175 * this function can be called after the constructor has
176 * prepared everyhing. it will load the index.tpl smarty
177 * template. if necessary it will registere pre-selects
178 * (photo index, photo, tag search, date search) into
181 public function show()
183 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
184 $this->tmpl->assign('page_title', $this->cfg->page_title);
185 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
186 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
188 if(isset($_GET['mode'])) {
190 $_SESSION['start_action'] = $_GET['mode'];
192 switch($_GET['mode']) {
194 if(isset($_GET['tags'])) {
195 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
197 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
198 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
200 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
201 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
205 if(isset($_GET['tags'])) {
206 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
207 $_SESSION['start_action'] = 'showp';
209 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
210 $_SESSION['current_photo'] = $_GET['id'];
211 $_SESSION['start_action'] = 'showp';
213 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
214 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
216 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
217 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
221 $this->tmpl->show("export.tpl");
225 $this->tmpl->show("slideshow.tpl");
229 if(isset($_GET['tags'])) {
230 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
232 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
233 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
235 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
236 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
244 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
245 $this->tmpl->assign('date_search_enabled', true);
247 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
248 $this->tmpl->assign('from_date', $this->get_calendar('from'));
249 $this->tmpl->assign('to_date', $this->get_calendar('to'));
250 $this->tmpl->assign('content_page', 'welcome.tpl');
251 $this->tmpl->show("index.tpl");
256 * get_tags - grab all tags of f-spot's database
258 * this function will get all available tags from
259 * the f-spot database and store them within two
260 * arrays within this class for later usage. in
261 * fact, if the user requests (hide_tags) it will
262 * opt-out some of them.
264 * this function is getting called once by show()
266 private function get_tags()
268 $this->avail_tags = Array();
271 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
274 DISTINCT t1.id as id, t1.name as name
277 INNER JOIN photo_tags
278 pt2 ON pt1.photo_id=pt2.photo_id
284 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
286 t1.sort_priority ASC";
288 $result = $this->db->db_query($query_str);
292 $result = $this->db->db_query("
295 ORDER BY sort_priority ASC
299 while($row = $this->db->db_fetch_object($result)) {
301 $tag_id = $row['id'];
302 $tag_name = $row['name'];
304 /* if the user has specified to ignore this tag in phpfspot's
305 configuration, ignore it here so it does not get added to
308 if(in_array($row['name'], $this->cfg->hide_tags))
311 /* if you include the following if-clause and the user has specified
312 to only show certain tags which are specified in phpfspot's
313 configuration, ignore all others so they will not be added to the
315 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
316 !in_array($row['name'], $this->cfg->show_tags))
320 $this->tags[$tag_id] = $tag_name;
321 $this->avail_tags[$count] = $tag_id;
329 * extract all photo details
331 * retrieve all available details from f-spot's
332 * database and return them as object
334 public function get_photo_details($idx)
336 if($this->dbver < 9) {
338 SELECT p.id, p.name, p.time, p.directory_path, p.description
344 SELECT p.id, p.uri, p.time, p.description
349 /* if show_tags is set, only return details for photos which
350 are specified to be shown
352 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
354 INNER JOIN photo_tags pt
358 WHERE p.id='". $idx ."'
359 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
363 WHERE p.id='". $idx ."'
367 if($result = $this->db->db_query($query_str)) {
369 $row = $this->db->db_fetch_object($result);
371 if($this->dbver < 9) {
372 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
381 } // get_photo_details
384 * returns aligned photo names
386 * this function returns aligned (length) names for
387 * an specific photo. If the length of the name exceeds
388 * $limit the name will be shrinked (...)
390 public function getPhotoName($idx, $limit = 0)
392 if($details = $this->get_photo_details($idx)) {
393 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
394 $name = $this->shrink_text($long_name, $limit);
404 * shrink text according provided limit
406 * If the length of the name exceeds $limit the
407 * text will be shortend and some content in between
408 * will be replaced with "..."
410 private function shrink_text($text, $limit)
412 if($limit != 0 && strlen($text) > $limit) {
413 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
421 * translate f-spoth photo path
423 * as the full-qualified path recorded in the f-spot database
424 * is usally not the same as on the webserver, this function
425 * will replace the path with that one specified in the cfg
427 public function translate_path($path, $width = 0)
429 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
434 * control HTML ouput for a single photo
436 * this function provides all the necessary information
437 * for the single photo template.
439 public function showPhoto($photo)
441 /* get all photos from the current photo selection */
442 $all_photos = $this->getPhotoSelection();
443 $count = count($all_photos);
445 for($i = 0; $i < $count; $i++) {
447 // $get_next will be set, when the photo which has to
448 // be displayed has been found - this means that the
449 // next available is in fact the NEXT image (for the
451 if(isset($get_next)) {
452 $next_img = $all_photos[$i];
456 /* the next photo is our NEXT photo */
457 if($all_photos[$i] == $photo) {
461 $previous_img = $all_photos[$i];
464 if($photo == $all_photos[$i]) {
469 $details = $this->get_photo_details($photo);
476 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
477 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
479 if(!file_exists($orig_path)) {
480 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
484 if(!is_readable($orig_path)) {
485 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
489 /* If the thumbnail doesn't exist yet, try to create it */
490 if(!file_exists($thumb_path)) {
491 $this->gen_thumb($photo, true);
492 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
495 /* get mime-type, height and width from the original photo */
496 $info = getimagesize($orig_path);
498 /* get EXIF information if JPEG */
499 if($info['mime'] == "image/jpeg") {
500 $meta = $this->get_meta_informations($orig_path);
503 /* If EXIF data are available, use them */
504 if(isset($meta['ExifImageWidth'])) {
505 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
507 $meta_res = $info[0] ."x". $info[1];
510 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
511 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
512 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
514 $extern_link = "index.php?mode=showp&id=". $photo;
515 $current_tags = $this->getCurrentTags();
516 if($current_tags != "") {
517 $extern_link.= "&tags=". $current_tags;
519 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
520 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
523 $this->tmpl->assign('extern_link', $extern_link);
525 if(!file_exists($thumb_path)) {
526 $this->_error("Can't open file ". $thumb_path ."\n");
530 $info_thumb = getimagesize($thumb_path);
532 $this->tmpl->assign('description', $details['description']);
533 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
535 $this->tmpl->assign('width', $info_thumb[0]);
536 $this->tmpl->assign('height', $info_thumb[1]);
537 $this->tmpl->assign('ExifMadeOn', $meta_date);
538 $this->tmpl->assign('ExifMadeWith', $meta_make);
539 $this->tmpl->assign('ExifOrigResolution', $meta_res);
540 $this->tmpl->assign('ExifFileSize', $meta_size);
542 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
543 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
544 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
546 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
547 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
548 $this->tmpl->assign('current_img', $photo);
551 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
552 $this->tmpl->assign('prev_img', $previous_img);
556 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
557 $this->tmpl->assign('next_img', $next_img);
559 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
560 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
561 $this->tmpl->assign('photo_number', $i);
562 $this->tmpl->assign('photo_count', count($all_photos));
564 $this->tmpl->show("single_photo.tpl");
569 * all available tags and tag cloud
571 * this function outputs all available tags (time ordered)
572 * and in addition output them as tag cloud (tags which have
573 * many photos will appears more then others)
575 public function getAvailableTags()
577 /* retrive tags from database */
582 $result = $this->db->db_query("
583 SELECT tag_id as id, count(tag_id) as quantity
593 while($row = $this->db->db_fetch_object($result)) {
594 $tags[$row['id']] = $row['quantity'];
597 // change these font sizes if you will
598 $max_size = 125; // max font size in %
599 $min_size = 75; // min font size in %
602 $max_sat = hexdec('cc');
603 $min_sat = hexdec('44');
605 // get the largest and smallest array values
606 $max_qty = max(array_values($tags));
607 $min_qty = min(array_values($tags));
609 // find the range of values
610 $spread = $max_qty - $min_qty;
611 if (0 == $spread) { // we don't want to divide by zero
615 // determine the font-size increment
616 // this is the increase per tag quantity (times used)
617 $step = ($max_size - $min_size)/($spread);
618 $step_sat = ($max_sat - $min_sat)/($spread);
620 // loop through our tag array
621 foreach ($tags as $key => $value) {
623 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
626 // calculate CSS font-size
627 // find the $value in excess of $min_qty
628 // multiply by the font-size increment ($size)
629 // and add the $min_size set above
630 $size = $min_size + (($value - $min_qty) * $step);
631 // uncomment if you want sizes in whole %:
634 $color = $min_sat + ($value - $min_qty) * $step_sat;
640 if(isset($this->tags[$key])) {
641 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
646 $output = substr($output, 0, strlen($output)-2);
649 } // getAvailableTags()
652 * output all selected tags
654 * this function output all tags which have been selected
655 * by the user. the selected tags are stored in the
656 * session-variable $_SESSION['selected_tags']
658 public function getSelectedTags()
660 /* retrive tags from database */
665 foreach($this->avail_tags as $tag)
667 // return all selected tags
668 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
669 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
674 $output = substr($output, 0, strlen($output)-2);
678 return "no tags selected";
681 } // getSelectedTags()
684 * add tag to users session variable
686 * this function will add the specified to users current
687 * tag selection. if a date search has been made before
688 * it will be now cleared
690 public function addTag($tag)
692 if(!isset($_SESSION['selected_tags']))
693 $_SESSION['selected_tags'] = Array();
695 if(isset($_SESSION['searchfor_tag']))
696 unset($_SESSION['searchfor_tag']);
698 if(!in_array($tag, $_SESSION['selected_tags']))
699 array_push($_SESSION['selected_tags'], $tag);
707 * remove tag to users session variable
709 * this function removes the specified tag from
710 * users current tag selection
712 public function delTag($tag)
714 if(isset($_SESSION['searchfor_tag']))
715 unset($_SESSION['searchfor_tag']);
717 if(isset($_SESSION['selected_tags'])) {
718 $key = array_search($tag, $_SESSION['selected_tags']);
719 unset($_SESSION['selected_tags'][$key]);
720 sort($_SESSION['selected_tags']);
728 * reset tag selection
730 * if there is any tag selection, it will be
733 public function resetTags()
735 if(isset($_SESSION['selected_tags']))
736 unset($_SESSION['selected_tags']);
741 * returns the value for the autocomplet tag-search
743 public function get_xml_tag_list()
745 if(!isset($_GET['search']) || !is_string($_GET['search']))
746 $_GET['search'] = '';
751 /* retrive tags from database */
754 $matched_tags = Array();
756 header("Content-Type: text/xml");
758 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
759 $string.= "<results>\n";
761 foreach($this->avail_tags as $tag)
763 if(!empty($_GET['search']) &&
764 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
765 count($matched_tags) < $length) {
767 $count = $this->get_num_photos($tag);
770 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
773 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
779 /* if we have collected enough items, break out */
780 if(count($matched_tags) >= $length)
784 $string.= "</results>\n";
788 } // get_xml_tag_list()
794 * if a specific photo was requested (external link)
795 * unset the session variable now
797 public function resetPhotoView()
799 if(isset($_SESSION['current_photo']))
800 unset($_SESSION['current_photo']);
802 } // resetPhotoView();
807 * if any tag search has taken place, reset it now
809 public function resetTagSearch()
811 if(isset($_SESSION['searchfor_tag']))
812 unset($_SESSION['searchfor_tag']);
814 } // resetTagSearch()
819 * if any name search has taken place, reset it now
821 public function resetNameSearch()
823 if(isset($_SESSION['searchfor_name']))
824 unset($_SESSION['searchfor_name']);
826 } // resetNameSearch()
831 * if any date search has taken place, reset
834 public function resetDateSearch()
836 if(isset($_SESSION['from_date']))
837 unset($_SESSION['from_date']);
838 if(isset($_SESSION['to_date']))
839 unset($_SESSION['to_date']);
841 } // resetDateSearch();
844 * return all photo according selection
846 * this function returns all photos based on
847 * the tag-selection, tag- or date-search.
848 * the tag-search also has to take care of AND
849 * and OR conjunctions
851 public function getPhotoSelection()
853 $matched_photos = Array();
854 $additional_where_cond = "";
856 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
857 $from_date = $_SESSION['from_date'];
858 $to_date = $_SESSION['to_date'];
859 $additional_where_cond.= "
860 p.time>='". $from_date ."'
862 p.time<='". $to_date ."'
866 if(isset($_SESSION['searchfor_name'])) {
867 if($this->dbver < 9) {
868 $additional_where_cond.= "
870 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
872 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
877 $additional_where_cond.= "
879 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
881 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
887 if(isset($_SESSION['sort_order'])) {
888 $order_str = $this->get_sort_order();
891 /* return a search result */
892 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
894 SELECT DISTINCT pt1.photo_id
896 INNER JOIN photo_tags pt2
897 ON pt1.photo_id=pt2.photo_id
904 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
906 if(isset($additional_where_cond) && !empty($additional_where_cond))
907 $query_str.= "AND ". $additional_where_cond ." ";
909 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
910 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
913 if(isset($order_str))
914 $query_str.= $order_str;
916 $result = $this->db->db_query($query_str);
917 while($row = $this->db->db_fetch_object($result)) {
918 array_push($matched_photos, $row['photo_id']);
920 return $matched_photos;
923 /* return according the selected tags */
924 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
926 foreach($_SESSION['selected_tags'] as $tag)
927 $selected.= $tag .",";
928 $selected = substr($selected, 0, strlen($selected)-1);
930 /* photo has to match at least on of the selected tags */
931 if($_SESSION['tag_condition'] == 'or') {
933 SELECT DISTINCT pt1.photo_id
935 INNER JOIN photo_tags pt2
936 ON pt1.photo_id=pt2.photo_id
941 WHERE pt1.tag_id IN (". $selected .")
943 if(isset($additional_where_cond) && !empty($additional_where_cond))
944 $query_str.= "AND ". $additional_where_cond ." ";
946 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
947 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
950 if(isset($order_str))
951 $query_str.= $order_str;
953 /* photo has to match all selected tags */
954 elseif($_SESSION['tag_condition'] == 'and') {
956 if(count($_SESSION['selected_tags']) >= 32) {
957 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
958 print "evaluate your tag selection. Please remove some tags from your selection.\n";
962 /* Join together a table looking like
964 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
966 so the query can quickly return all images matching the
967 selected tags in an AND condition
972 SELECT DISTINCT pt1.photo_id
976 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
983 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
985 INNER JOIN photo_tags pt". ($i+2) ."
986 ON pt1.photo_id=pt". ($i+2) .".photo_id
993 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
994 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
996 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
999 if(isset($additional_where_cond) && !empty($additional_where_cond))
1000 $query_str.= "AND ". $additional_where_cond;
1002 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1003 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1006 if(isset($order_str))
1007 $query_str.= $order_str;
1011 $result = $this->db->db_query($query_str);
1012 while($row = $this->db->db_fetch_object($result)) {
1013 array_push($matched_photos, $row['photo_id']);
1015 return $matched_photos;
1018 /* return all available photos */
1020 SELECT DISTINCT p.id
1022 LEFT JOIN photo_tags pt
1028 if(isset($additional_where_cond) && !empty($additional_where_cond))
1029 $query_str.= "WHERE ". $additional_where_cond ." ";
1031 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1032 if(isset($additional_where_cond) && !empty($additional_where_cond))
1033 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1035 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1038 if(isset($order_str))
1039 $query_str.= $order_str;
1041 $result = $this->db->db_query($query_str);
1042 while($row = $this->db->db_fetch_object($result)) {
1043 array_push($matched_photos, $row['id']);
1045 return $matched_photos;
1047 } // getPhotoSelection()
1050 * control HTML ouput for photo index
1052 * this function provides all the necessary information
1053 * for the photo index template.
1055 public function showPhotoIndex()
1057 $photos = $this->getPhotoSelection();
1059 $count = count($photos);
1061 /* if all thumbnails should be shown on one page */
1062 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1066 /* thumbnails should be splitted up in several pages */
1067 elseif($this->cfg->thumbs_per_page > 0) {
1069 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1073 $begin_with = $_SESSION['begin_with'];
1076 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1080 $images[$thumbs] = Array();
1081 $img_height[$thumbs] = Array();
1082 $img_width[$thumbs] = Array();
1083 $img_id[$thumbs] = Array();
1084 $img_name[$thumbs] = Array();
1085 $img_fullname[$thumbs] = Array();
1086 $img_title = Array();
1088 for($i = $begin_with; $i < $end_with; $i++) {
1090 if(isset($photos[$i])) {
1092 $images[$thumbs] = $photos[$i];
1093 $img_id[$thumbs] = $i;
1094 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1095 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1096 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1098 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1100 if(file_exists($thumb_path)) {
1101 $info = getimagesize($thumb_path);
1102 $img_width[$thumbs] = $info[0];
1103 $img_height[$thumbs] = $info[1];
1109 // +1 for for smarty's selection iteration
1112 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1113 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1115 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1116 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1117 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1120 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1121 $this->tmpl->assign('tag_result', 1);
1124 /* do we have to display the page selector ? */
1125 if($this->cfg->thumbs_per_page != 0) {
1129 /* calculate the page switchers */
1130 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1131 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1133 if($begin_with != 0)
1134 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1135 if($end_with < $count)
1136 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1138 $photo_per_page = $this->cfg->thumbs_per_page;
1139 $last_page = ceil($count / $photo_per_page);
1141 /* get the current selected page */
1142 if($begin_with == 0) {
1146 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1153 for($i = 1; $i <= $last_page; $i++) {
1155 if($current_page == $i)
1156 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1157 elseif($current_page-1 == $i || $current_page+1 == $i)
1158 $style = "style=\"font-size: 105%;\"";
1159 elseif(($current_page-5 >= $i) && ($i != 1) ||
1160 ($current_page+5 <= $i) && ($i != $last_page))
1161 $style = "style=\"font-size: 75%;\"";
1165 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1168 $select.= ">". $i ."</a> ";
1170 // until 9 pages we show the selector from 1-9
1171 if($last_page <= 9) {
1172 $page_select.= $select;
1175 if($i == 1 /* first page */ ||
1176 $i == $last_page /* last page */ ||
1177 $i == $current_page /* current page */ ||
1178 $i == ceil($last_page * 0.25) /* first quater */ ||
1179 $i == ceil($last_page * 0.5) /* half */ ||
1180 $i == ceil($last_page * 0.75) /* third quater */ ||
1181 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1182 (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 */ ||
1183 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1184 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1186 $page_select.= $select;
1194 $page_select.= "......... ";
1199 /* only show the page selector if we have more then one page */
1201 $this->tmpl->assign('page_selector', $page_select);
1205 $current_tags = $this->getCurrentTags();
1206 $extern_link = "index.php?mode=showpi";
1207 $rss_link = "index.php?mode=rss";
1208 if($current_tags != "") {
1209 $extern_link.= "&tags=". $current_tags;
1210 $rss_link.= "&tags=". $current_tags;
1212 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1213 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1214 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1217 $export_link = "index.php?mode=export";
1218 $slideshow_link = "index.php?mode=slideshow";
1220 $this->tmpl->assign('extern_link', $extern_link);
1221 $this->tmpl->assign('slideshow_link', $slideshow_link);
1222 $this->tmpl->assign('export_link', $export_link);
1223 $this->tmpl->assign('rss_link', $rss_link);
1224 $this->tmpl->assign('count', $count);
1225 $this->tmpl->assign('width', $this->cfg->thumb_width);
1226 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1227 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1228 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1229 $this->tmpl->assign('images', $images);
1230 $this->tmpl->assign('img_width', $img_width);
1231 $this->tmpl->assign('img_height', $img_height);
1232 $this->tmpl->assign('img_id', $img_id);
1233 $this->tmpl->assign('img_name', $img_name);
1234 $this->tmpl->assign('img_fullname', $img_fullname);
1235 $this->tmpl->assign('img_title', $img_title);
1236 $this->tmpl->assign('thumbs', $thumbs);
1238 $this->tmpl->show("photo_index.tpl");
1240 /* if we are returning to photo index from an photo-view,
1241 scroll the window to the last shown photo-thumbnail.
1242 after this, unset the last_photo session variable.
1244 if(isset($_SESSION['last_photo'])) {
1245 print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1246 unset($_SESSION['last_photo']);
1249 } // showPhotoIndex()
1252 * show credit template
1254 public function showCredits()
1256 $this->tmpl->assign('version', $this->cfg->version);
1257 $this->tmpl->assign('product', $this->cfg->product);
1258 $this->tmpl->assign('db_version', $this->dbver);
1259 $this->tmpl->show("credits.tpl");
1264 * create_thumbnails for the requested width
1266 * this function creates image thumbnails of $orig_image
1267 * stored as $thumb_image. It will check if the image is
1268 * in a supported format, if necessary rotate the image
1269 * (based on EXIF orientation meta headers) and re-sizing.
1271 public function create_thumbnail($orig_image, $thumb_image, $width)
1273 if(!file_exists($orig_image)) {
1277 $details = getimagesize($orig_image);
1279 /* check if original photo is a support image type */
1280 if(!$this->checkifImageSupported($details['mime']))
1283 switch($details['mime']) {
1287 $meta = $this->get_meta_informations($orig_image);
1293 switch($meta['Orientation']) {
1294 case 1: /* top, left */
1295 /* nothing to do */ break;
1296 case 2: /* top, right */
1297 $rotate = 0; $flip_hori = true; break;
1298 case 3: /* bottom, left */
1299 $rotate = 180; break;
1300 case 4: /* bottom, right */
1301 $flip_vert = true; break;
1302 case 5: /* left side, top */
1303 $rotate = 90; $flip_vert = true; break;
1304 case 6: /* right side, top */
1305 $rotate = 90; break;
1306 case 7: /* left side, bottom */
1307 $rotate = 270; $flip_vert = true; break;
1308 case 8: /* right side, bottom */
1309 $rotate = 270; break;
1312 $src_img = @imagecreatefromjpeg($orig_image);
1317 $src_img = @imagecreatefrompng($orig_image);
1323 print "Can't load image from ". $orig_image ."\n";
1327 /* grabs the height and width */
1328 $cur_width = imagesx($src_img);
1329 $cur_height = imagesy($src_img);
1331 // If requested width is more then the actual image width,
1332 // do not generate a thumbnail, instead safe the original
1333 // as thumbnail but with lower quality. But if the image
1334 // is to heigh too, then we still have to resize it.
1335 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1336 $result = imagejpeg($src_img, $thumb_image, 75);
1337 imagedestroy($src_img);
1341 // If the image will be rotate because EXIF orientation said so
1342 // 'virtually rotate' the image for further calculations
1343 if($rotate == 90 || $rotate == 270) {
1345 $cur_width = $cur_height;
1349 /* calculates aspect ratio */
1350 $aspect_ratio = $cur_height / $cur_width;
1353 if($aspect_ratio < 1) {
1355 $new_h = abs($new_w * $aspect_ratio);
1357 /* 'virtually' rotate the image and calculate it's ratio */
1358 $tmp_w = $cur_height;
1359 $tmp_h = $cur_width;
1360 /* now get the ratio from the 'rotated' image */
1361 $tmp_ratio = $tmp_h/$tmp_w;
1362 /* now calculate the new dimensions */
1364 $tmp_h = abs($tmp_w * $tmp_ratio);
1366 // now that we know, how high they photo should be, if it
1367 // gets rotated, use this high to scale the image
1369 $new_w = abs($new_h / $aspect_ratio);
1371 // If the image will be rotate because EXIF orientation said so
1372 // now 'virtually rotate' back the image for the image manipulation
1373 if($rotate == 90 || $rotate == 270) {
1380 /* creates new image of that size */
1381 $dst_img = imagecreatetruecolor($new_w, $new_h);
1383 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1385 /* copies resized portion of original image into new image */
1386 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1388 /* needs the image to be flipped horizontal? */
1390 $this->_debug("(FLIP)");
1391 $dst_img = $this->flipImage($dst_img, 'hori');
1393 /* needs the image to be flipped vertical? */
1395 $this->_debug("(FLIP)");
1396 $dst_img = $this->flipImage($dst_img, 'vert');
1400 $this->_debug("(ROTATE)");
1401 $dst_img = $this->rotateImage($dst_img, $rotate);
1404 /* write down new generated file */
1405 $result = imagejpeg($dst_img, $thumb_image, 75);
1407 /* free your mind */
1408 imagedestroy($dst_img);
1409 imagedestroy($src_img);
1411 if($result === false) {
1412 print "Can't write thumbnail ". $thumb_image ."\n";
1418 } // create_thumbnail()
1421 * return all exif meta data from the file
1423 public function get_meta_informations($file)
1425 return exif_read_data($file);
1427 } // get_meta_informations()
1430 * create phpfspot own sqlite database
1432 * this function creates phpfspots own sqlite database
1433 * if it does not exist yet. this own is used to store
1434 * some necessary informations (md5 sum's, ...).
1436 public function check_config_table()
1438 // if the config table doesn't exist yet, create it
1439 if(!$this->cfg_db->db_check_table_exists("images")) {
1440 $this->cfg_db->db_exec("
1441 CREATE TABLE images (
1442 img_idx int primary key,
1448 } // check_config_table
1451 * Generates a thumbnail from photo idx
1453 * This function will generate JPEG thumbnails from provided F-Spot photo
1456 * 1. Check if all thumbnail generations (width) are already in place and
1458 * 2. Check if the md5sum of the original file has changed
1459 * 3. Generate the thumbnails if needed
1461 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1465 $resolutions = Array(
1466 $this->cfg->thumb_width,
1467 $this->cfg->photo_width,
1468 $this->cfg->mini_width,
1471 /* get details from F-Spot's database */
1472 $details = $this->get_photo_details($idx);
1474 /* calculate file MD5 sum */
1475 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1477 if(!file_exists($full_path)) {
1478 $this->_error("File ". $full_path ." does not exist\n");
1482 if(!is_readable($full_path)) {
1483 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1487 $file_md5 = md5_file($full_path);
1489 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1493 foreach($resolutions as $resolution) {
1495 $generate_it = false;
1497 $thumb_sub_path = substr($file_md5, 0, 2);
1498 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1500 /* if thumbnail-subdirectory does not exist yet, create it */
1501 if(!file_exists(dirname($thumb_path))) {
1502 mkdir(dirname($thumb_path), 0755);
1505 /* if the thumbnail file doesn't exist, create it */
1506 if(!file_exists($thumb_path)) {
1507 $generate_it = true;
1509 /* if the file hasn't changed there is no need to regen the thumb */
1510 elseif($file_md5 != $this->getMD5($idx) || $force) {
1511 $generate_it = true;
1514 if($generate_it || $overwrite) {
1516 $this->_debug(" ". $resolution ."px");
1517 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1525 $this->_debug(" already exist");
1528 /* set the new/changed MD5 sum for the current photo */
1530 $this->setMD5($idx, $file_md5);
1533 $this->_debug("\n");
1538 * returns stored md5 sum for a specific photo
1540 * this function queries the phpfspot database for a
1541 * stored MD5 checksum of the specified photo
1543 public function getMD5($idx)
1545 $result = $this->cfg_db->db_query("
1548 WHERE img_idx='". $idx ."'
1554 $img = $this->cfg_db->db_fetch_object($result);
1555 return $img['img_md5'];
1560 * set MD5 sum for the specific photo
1562 private function setMD5($idx, $md5)
1564 $result = $this->cfg_db->db_exec("
1565 REPLACE INTO images (img_idx, img_md5)
1566 VALUES ('". $idx ."', '". $md5 ."')
1572 * store current tag condition
1574 * this function stores the current tag condition
1575 * (AND or OR) in the users session variables
1577 public function setTagCondition($mode)
1579 $_SESSION['tag_condition'] = $mode;
1583 } // setTagCondition()
1586 * invoke tag & date search
1588 * this function will return all matching tags and store
1589 * them in the session variable selected_tags. furthermore
1590 * it also handles the date search.
1591 * getPhotoSelection() will then only return the matching
1594 public function startSearch()
1596 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1597 $from = $_POST['from'];
1599 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1603 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1604 $searchfor_tag = $_POST['for_tag'];
1605 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1608 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1609 $searchfor_name = $_POST['for_name'];
1610 $_SESSION['searchfor_name'] = $_POST['for_name'];
1615 if(isset($from) && !empty($from))
1616 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1618 unset($_SESSION['from_date']);
1620 if(isset($to) && !empty($to))
1621 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1623 unset($_SESSION['to_date']);
1625 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1626 /* new search, reset the current selected tags */
1627 $_SESSION['selected_tags'] = Array();
1628 foreach($this->avail_tags as $tag) {
1629 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1630 array_push($_SESSION['selected_tags'], $tag);
1639 * updates sort order in session variable
1641 * this function is invoked by RPC and will sort the requested
1642 * sort order in the session variable.
1644 public function updateSortOrder($order)
1646 if(isset($this->sort_orders[$order])) {
1647 $_SESSION['sort_order'] = $order;
1651 return "unkown error";
1653 } // updateSortOrder()
1658 * this function rotates the image according the
1661 private function rotateImage($img, $degrees)
1663 if(function_exists("imagerotate")) {
1664 $img = imagerotate($img, $degrees, 0);
1666 function imagerotate($src_img, $angle)
1668 $src_x = imagesx($src_img);
1669 $src_y = imagesy($src_img);
1670 if ($angle == 180) {
1674 elseif ($src_x <= $src_y) {
1678 elseif ($src_x >= $src_y) {
1683 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1684 imagealphablending($rotate, false);
1689 for ($y = 0; $y < ($src_y); $y++) {
1690 for ($x = 0; $x < ($src_x); $x++) {
1691 $color = imagecolorat($src_img, $x, $y);
1692 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1698 for ($y = 0; $y < ($src_y); $y++) {
1699 for ($x = 0; $x < ($src_x); $x++) {
1700 $color = imagecolorat($src_img, $x, $y);
1701 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1707 for ($y = 0; $y < ($src_y); $y++) {
1708 for ($x = 0; $x < ($src_x); $x++) {
1709 $color = imagecolorat($src_img, $x, $y);
1710 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1724 $img = imagerotate($img, $degrees);
1733 * returns flipped image
1735 * this function will return an either horizontal or
1736 * vertical flipped truecolor image.
1738 private function flipImage($image, $mode)
1740 $w = imagesx($image);
1741 $h = imagesy($image);
1742 $flipped = imagecreatetruecolor($w, $h);
1746 for ($y = 0; $y < $h; $y++) {
1747 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1751 for ($x = 0; $x < $w; $x++) {
1752 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1762 * return all assigned tags for the specified photo
1764 private function get_photo_tags($idx)
1766 $result = $this->db->db_query("
1769 INNER JOIN photo_tags pt
1771 WHERE pt.photo_id='". $idx ."'
1776 while($row = $this->db->db_fetch_object($result))
1777 $tags[$row['id']] = $row['name'];
1781 } // get_photo_tags()
1784 * create on-the-fly images with text within
1786 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1788 if (strlen($color) != 6)
1791 $int = hexdec($color);
1792 $h = imagefontheight($font);
1793 $fw = imagefontwidth($font);
1794 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1795 $lines = count($txt);
1796 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1797 $bg = imagecolorallocate($im, 255, 255, 255);
1798 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1801 foreach ($txt as $text) {
1802 $x = (($w - ($fw * strlen($text))) / 2);
1803 imagestring($im, $font, $x, $y, $text, $color);
1804 $y += ($h + $space);
1807 Header("Content-type: image/png");
1810 } // showTextImage()
1813 * check if all requirements are met
1815 private function check_requirements()
1817 if(!function_exists("imagecreatefromjpeg")) {
1818 print "PHP GD library extension is missing<br />\n";
1822 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1823 print "PHP SQLite3 library extension is missing<br />\n";
1827 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1828 ini_set('track_errors', 1);
1829 @include_once 'HTML/AJAX/Server.php';
1830 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1831 print "PEAR HTML_AJAX package is missing<br />\n";
1834 @include_once 'Calendar/Calendar.php';
1835 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1836 print "PEAR Calendar package is missing<br />\n";
1839 @include_once 'Console/Getopt.php';
1840 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1841 print "PEAR Console_Getopt package is missing<br />\n";
1844 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
1845 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1846 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
1849 ini_restore('track_errors');
1856 } // check_requirements()
1858 private function _debug($text)
1860 if($this->fromcmd) {
1867 * check if specified MIME type is supported
1869 public function checkifImageSupported($mime)
1871 if(in_array($mime, Array("image/jpeg", "image/png")))
1876 } // checkifImageSupported()
1878 public function _error($text)
1880 switch($this->cfg->logging) {
1883 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1884 print $text ."<br />\n";
1890 error_log($text, 3, $his->cfg->log_file);
1894 $this->runtime_error = true;
1899 * output calendard input fields
1901 private function get_calendar($mode)
1903 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1904 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1905 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1907 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1908 if(!isset($_SESSION[$mode .'_date']))
1909 $output.= " disabled=\"disabled\"";
1911 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1912 if(!isset($_SESSION[$mode .'_date']))
1913 $output.= " disabled=\"disabled\"";
1915 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1916 if(!isset($_SESSION[$mode .'_date']))
1917 $output.= " disabled=\"disabled\"";
1925 * output calendar matrix
1927 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1929 if (!isset($year)) $year = date('Y');
1930 if (!isset($month)) $month = date('m');
1931 if (!isset($day)) $day = date('d');
1936 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1937 require_once CALENDAR_ROOT.'Day.php';
1940 $month = new Calendar_Month_Weekdays($year,$month);
1943 $prevStamp = $month->prevMonth(true);
1944 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1945 $nextStamp = $month->nextMonth(true);
1946 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1948 $selectedDays = array (
1949 new Calendar_Day($year,$month,$day),
1950 new Calendar_Day($year,12,25),
1953 // Build the days in the month
1954 $month->build($selectedDays);
1956 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1957 $this->tmpl->assign('prev_month', $prev);
1958 $this->tmpl->assign('next_month', $next);
1960 while ( $day = $month->fetch() ) {
1962 if(!isset($matrix[$rows]))
1963 $matrix[$rows] = Array();
1967 $dayStamp = $day->thisDay(true);
1968 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1970 // isFirst() to find start of week
1971 if ( $day->isFirst() )
1974 if ( $day->isSelected() ) {
1975 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1976 } else if ( $day->isEmpty() ) {
1977 $string.= "<td> </td>\n";
1979 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1982 // isLast() to find end of week
1983 if ( $day->isLast() )
1984 $string.= "</tr>\n";
1986 $matrix[$rows][$cols] = $string;
1996 $this->tmpl->assign('matrix', $matrix);
1997 $this->tmpl->assign('rows', $rows);
1998 $this->tmpl->show("calendar.tpl");
2000 } // get_calendar_matrix()
2003 * output export page
2005 public function getExport($mode)
2007 $pictures = $this->getPhotoSelection();
2008 $current_tags = $this->getCurrentTags();
2010 foreach($pictures as $picture) {
2012 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2013 if($current_tags != "") {
2014 $orig_url.= "&tags=". $current_tags;
2016 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2017 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2020 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2025 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2026 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2030 // "[%pictureurl% %thumbnailurl%]"
2031 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2034 case 'MoinMoinList':
2035 // " * [%pictureurl% %thumbnailurl%]"
2036 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2047 public function getRSSFeed()
2049 Header("Content-type: text/xml; charset=utf-8");
2050 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2053 xmlns:media="http://search.yahoo.com/mrss/"
2054 xmlns:dc="http://purl.org/dc/elements/1.1/"
2057 <title>phpfspot</title>
2058 <description>phpfspot RSS feed</description>
2059 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2060 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2061 <generator>phpfspot</generator>
2064 $pictures = $this->getPhotoSelection();
2065 $current_tags = $this->getCurrentTags();
2067 foreach($pictures as $picture) {
2069 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2070 if($current_tags != "") {
2071 $orig_url.= "&tags=". $current_tags;
2073 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2074 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2077 $details = $this->get_photo_details($picture);
2079 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2080 $thumb_html = htmlspecialchars("
2081 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2083 ". $details['description']);
2085 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2087 /* get EXIF information if JPEG */
2088 if($details['mime'] == "image/jpeg") {
2089 $meta = $this->get_meta_informations($orig_path);
2092 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2096 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2097 <link><?php print htmlspecialchars($orig_url); ?></link>
2098 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2099 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2101 <?php print $thumb_html; ?>
2103 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2118 * return all selected tags as one string
2120 private function getCurrentTags()
2123 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2124 foreach($_SESSION['selected_tags'] as $tag)
2125 $current_tags.= $tag .",";
2126 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2128 return $current_tags;
2130 } // getCurrentTags()
2133 * return the current photo
2135 public function getCurrentPhoto()
2137 if(isset($_SESSION['current_photo'])) {
2138 print $_SESSION['current_photo'];
2140 } // getCurrentPhoto()
2143 * tells the client browser what to do
2145 * this function is getting called via AJAX by the
2146 * client browsers. it will tell them what they have
2147 * to do next. This is necessary for directly jumping
2148 * into photo index or single photo view when the are
2149 * requested with specific URLs
2151 public function whatToDo()
2153 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2154 return "show_photo";
2156 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2157 return "showpi_tags";
2159 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2163 return "nothing special";
2168 * return the current process-user
2170 private function getuid()
2172 if($uid = posix_getuid()) {
2173 if($user = posix_getpwuid($uid)) {
2174 return $user['name'];
2183 * returns a select-dropdown box to select photo index sort parameters
2185 public function smarty_sort_select_list($params, &$smarty)
2189 foreach($this->sort_orders as $key => $value) {
2190 $output.= "<option value=\"". $key ."\"";
2191 if($key == $_SESSION['sort_order']) {
2192 $output.= " selected=\"selected\"";
2194 $output.= ">". $value ."</option>";
2199 } // smarty_sort_select_list()
2202 * returns the currently selected sort order
2204 private function get_sort_order()
2206 switch($_SESSION['sort_order']) {
2208 return " ORDER BY p.time ASC";
2211 return " ORDER BY p.time DESC";
2214 if($this->dbver < 9) {
2215 return " ORDER BY p.name ASC";
2218 return " ORDER BY basename(p.uri) ASC";
2222 if($this->dbver < 9) {
2223 return " ORDER BY p.name DESC";
2226 return " ORDER BY basename(p.uri) DESC";
2230 return " ORDER BY t.name ASC ,p.time ASC";
2233 return " ORDER BY t.name DESC ,p.time ASC";
2237 } // get_sort_order()
2240 * return the next to be shown slide show image
2242 * this function returns the URL of the next image
2243 * in the slideshow sequence.
2245 public function getNextSlideShowImage()
2247 $all_photos = $this->getPhotoSelection();
2249 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2250 $_SESSION['slideshow_img'] = 0;
2252 $_SESSION['slideshow_img']++;
2254 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2256 } // getNextSlideShowImage()
2259 * return the previous to be shown slide show image
2261 * this function returns the URL of the previous image
2262 * in the slideshow sequence.
2264 public function getPrevSlideShowImage()
2266 $all_photos = $this->getPhotoSelection();
2268 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2269 $_SESSION['slideshow_img'] = 0;
2271 $_SESSION['slideshow_img']--;
2273 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2275 } // getPrevSlideShowImage()
2277 public function resetSlideShow()
2279 if(isset($_SESSION['slideshow_img']))
2280 unset($_SESSION['slideshow_img']);
2282 } // resetSlideShow()
2287 * this function will get all photos from the fspot
2288 * database and randomly return ONE entry
2290 * saddly there is yet no sqlite3 function which returns
2291 * the bulk result in array, so we have to fill up our
2294 public function get_random_photo()
2298 $result = $this->db->db_query("
2303 while($row = $this->db->db_fetch_object($result)) {
2304 array_push($all, $row['id']);
2307 return $all[array_rand($all)];
2309 } // get_random_photo()
2312 * validates provided date
2314 * this function validates if the provided date
2315 * contains a valid date and will return true
2318 public function isValidDate($date_str)
2320 $timestamp = strtotime($date_str);
2322 if(is_numeric($timestamp))
2330 * timestamp to string conversion
2332 private function ts2str($timestamp)
2334 return strftime("%Y-%m-%d", $timestamp);
2337 private function extractTags($tags_str)
2339 $not_validated = split(',', $_GET['tags']);
2340 $validated = array();
2342 foreach($not_validated as $tag) {
2343 if(is_numeric($tag))
2344 array_push($validated, $tag);
2352 * returns the full path to a thumbnail
2354 public function get_thumb_path($width, $photo)
2356 $md5 = $this->getMD5($photo);
2357 $sub_path = substr($md5, 0, 2);
2358 return $this->cfg->thumb_path
2366 } // get_thumb_path()
2369 * returns server's virtual host name
2371 private function get_server_name()
2373 return $_SERVER['SERVER_NAME'];
2374 } // get_server_name()
2377 * returns type of webprotocol which is
2380 private function get_web_protocol()
2382 if(!isset($_SERVER['HTTPS']))
2386 } // get_web_protocol()
2389 * return url to this phpfspot installation
2391 private function get_phpfspot_url()
2393 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2394 } // get_phpfspot_url()
2397 * returns the number of photos which are tagged with $tag_id
2399 public function get_num_photos($tag_id)
2401 if($result = $this->db->db_fetchSingleRow("
2402 SELECT count(*) as number
2405 tag_id LIKE '". $tag_id ."'")) {
2407 return $result['number'];
2413 } // get_num_photos()
2416 * check file exists and is readable
2418 * returns true, if everything is ok, otherwise false
2419 * if $silent is not set, this function will output and
2422 private function check_readable($file, $silent = null)
2424 if(!file_exists($file)) {
2426 print "File \"". $file ."\" does not exist.\n";
2430 if(!is_readable($file)) {
2432 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2438 } // check_readable()
2441 * check if all needed indices are present
2443 * this function checks, if some needed indices are already
2444 * present, or if not, create them on the fly. they are
2445 * necessary to speed up some queries like that one look for
2446 * all tags, when show_tags is specified in the configuration.
2448 private function checkDbIndices()
2450 $result = $this->db->db_exec("
2451 CREATE INDEX IF NOT EXISTS
2458 } // checkDbIndices()
2461 * retrive F-Spot database version
2463 * this function will return the F-Spot database version number
2464 * It is stored within the sqlite3 database in the table meta
2466 public function getFspotDBVersion()
2468 if($result = $this->db->db_fetchSingleRow("
2469 SELECT data as version
2472 name LIKE 'F-Spot Database Version'
2474 return $result['version'];
2478 } // getFspotDBVersion()
2481 * parse the provided URI and will returned the
2484 public function parse_uri($uri, $mode)
2486 if(($components = parse_url($uri)) !== false) {
2490 return basename($components['path']);
2493 return dirname($components['path']);
2496 return $components['path'];
2506 * validate config options
2508 * this function checks if all necessary configuration options are
2509 * specified and set.
2511 private function check_config_options()
2513 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2514 $this->_error("Please set \$page_title in phpfspot_cfg");
2516 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2517 $this->_error("Please set \$base_path in phpfspot_cfg");
2519 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2520 $this->_error("Please set \$web_path in phpfspot_cfg");
2522 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2523 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2525 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2526 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2528 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2529 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2531 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2532 $this->_error("Please set \$db_access in phpfspot_cfg");
2534 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2535 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2537 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2538 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2540 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2541 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2543 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2544 $this->_error("Please set \$photo_width in phpfspot_cfg");
2546 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2547 $this->_error("Please set \$mini_width in phpfspot_cfg");
2549 if(!isset($this->cfg->thumbs_per_page))
2550 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2552 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2553 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2555 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2556 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2558 if(!isset($this->cfg->hide_tags))
2559 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2561 if(!isset($this->cfg->theme_name))
2562 $this->_error("Please set \$theme_name in phpfspot_cfg");
2564 if(!isset($this->cfg->logging))
2565 $this->_error("Please set \$logging in phpfspot_cfg");
2567 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2569 if(!isset($this->cfg->log_file))
2570 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2572 if(!is_writeable($this->cfg->log_file))
2573 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2577 /* check for pending slash on web_path */
2578 if(!preg_match("/\/$/", $this->cfg->web_path))
2579 $this->cfg->web_path.= "/";
2581 return $this->runtime_error;
2583 } // check_config_options()
2586 * cleanup phpfspot own database
2588 * When photos are getting delete from F-Spot, there will remain
2589 * remain some residues in phpfspot own database. This function
2590 * will try to wipe them out.
2592 public function cleanup_phpfspot_db()
2594 $to_delete = Array();
2596 $result = $this->cfg_db->db_query("
2599 ORDER BY img_idx ASC
2602 while($row = $this->cfg_db->db_fetch_object($result)) {
2603 if(!$this->db->db_fetchSingleRow("
2606 WHERE id='". $row['img_idx'] ."'")) {
2608 array_push($to_delete, $row['img_idx'], ',');
2612 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2614 $this->cfg_db->db_exec("
2616 WHERE img_idx IN (". implode($to_delete) .")
2619 } // cleanup_phpfspot_db()
2622 * return first image of the page, the $current photo
2625 * this function is used to find out the first photo of the
2626 * current page, in which the $current photo lies. this is
2627 * used to display the correct photo, when calling showPhotoIndex()
2630 private function getCurrentPage($current, $max)
2632 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
2633 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
2634 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
2640 } // getCurrentPage()