cfg = new PHPFSPOT_CFG; /* verify config settings */ if($this->check_config_options()) { exit(1); } /* set application name and version information */ $this->cfg->product = "phpfspot"; $this->cfg->version = "1.7"; $this->cfg->db_version = 2; $this->sort_orders= array( 'date_asc' => 'Date ↑', 'date_desc' => 'Date ↓', 'name_asc' => 'Name ↑', 'name_desc' => 'Name ↓', 'tags_asc' => 'Tags ↑', 'tags_desc' => 'Tags ↓', ); /* Check necessary requirements */ if(!$this->check_requirements()) { exit(1); } /******* Opening F-Spot's sqlite database *********/ /* Check if database file exists and is readable */ if(!file_exists($this->cfg->fspot_db) || !is_readable($this->cfg->fspot_db)) { print "Error: ". $this->cfg->fspot_db ." does not exist or is not readable for user ". $this->getuid() .".\n"; exit(1); } /* Check if database file is writeable */ if(!is_writeable($this->cfg->fspot_db)) { print "Error: ". $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() .".\n"; print "Please fix permissions so phpfspot can create indices within the F-Spot database to" ." speed up some database operations.\n"; exit(1); } /* open the database */ $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db); /* change sqlite temp directory, if requested */ if(isset($this->cfg->sqlite_temp_dir)) { $this->db->db_exec(" PRAGMA temp_store_directory = '". $this->cfg->sqlite_temp_dir ."' "); } /* get F-Spot database version */ $this->dbver = $this->getFspotDBVersion(); if(!is_writeable($this->cfg->base_path ."/templates_c")) { print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n"; exit(1); } if(!is_writeable($this->cfg->thumb_path)) { print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n"; exit(1); } /******* Opening phpfspot's sqlite database *********/ /* Check if directory where the database file is stored is writeable */ if(!is_writeable(dirname($this->cfg->phpfspot_db))) { print "Error: ". dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() .".\n"; print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n"; exit(1); } /* Check if database file is writeable */ if(file_exists($this->cfg->phpfspot_db) && !is_writeable($this->cfg->phpfspot_db)) { print "Error: ". $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() .".\n"; print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n"; exit(1); } /* open the database */ $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db); /* change sqlite temp directory, if requested */ if(isset($this->cfg->sqlite_temp_dir)) { $this->cfg_db->db_exec(" PRAGMA temp_store_directory = '". $this->cfg->sqlite_temp_dir ."' "); } /* Check if some tables need to be created */ $this->check_phpfspot_db(); /* overload Smarty class with our own template handler */ require_once "phpfspot_tmpl.php"; $this->tmpl = new PHPFSPOT_TMPL(); /* pre-set some template variables */ $this->tmpl->assign('web_path', $this->cfg->web_path); /* Starting with F-Spot 0.4.2, the rating-feature was available */ if($this->dbver > 10) { $this->tmpl->assign('has_rating', true); $this->sort_orders = array_merge($this->sort_orders, array( 'rate_asc' => 'Rate ↑', 'rate_desc' => 'Rate ↓', )); } /* check if all necessary indices exist */ $this->checkDbIndices(); /* if session is not yet started, do it now */ if(session_id() == "") session_start(); if(!isset($_SESSION['tag_condition'])) $_SESSION['tag_condition'] = 'or'; /* if sort-order has not been set yet, get the one specified in the config */ if(!isset($_SESSION['sort_order'])) $_SESSION['sort_order'] = $this->cfg->sort_order; if(!isset($_SESSION['searchfor_tag'])) $_SESSION['searchfor_tag'] = ''; // if begin_with is still set but thumbs_per_page is now 0, unset it if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0) unset($_SESSION['begin_with']); // if user-friendly-url's are enabled, set also a flag for the template handler if($this->is_user_friendly_url()) { $this->tmpl->assign('user_friendly_url', 'true'); } } // __construct() public function __destruct() { } // __destruct() /** * show - generate html output * * this function can be called after the constructor has * prepared everyhing. it will load the index.tpl smarty * template. if necessary it will registere pre-selects * (photo index, photo, tag search, date search) into * users session. */ public function show() { $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']); $this->tmpl->assign('page_title', $this->cfg->page_title); $this->tmpl->assign('current_condition', $_SESSION['tag_condition']); $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name); /* parse URL */ if($this->is_user_friendly_url()) { $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']); } if(isset($_GET['mode'])) { $_SESSION['start_action'] = $_GET['mode']; switch($_GET['mode']) { case 'showpi': if(isset($_GET['tags'])) { $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']); } if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) { $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00"); } if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) { $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59"); } break; case 'showp': if(isset($_GET['tags'])) { $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']); $_SESSION['start_action'] = 'showp'; } if(isset($_GET['id']) && is_numeric($_GET['id'])) { if($_SESSION['current_photo'] != $_GET['id']) unset($_SESSION['current_version']); $_SESSION['current_photo'] = $_GET['id']; $_SESSION['start_action'] = 'showp'; } if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) { $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00"); } if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) { $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59"); } break; case 'export': /* fetch export template */ print $this->tmpl->fetch("export.tpl"); /* no further execution necessary. */ return; break; case 'slideshow': /* fetch slideshow template */ print $this->tmpl->show("slideshow.tpl"); /* no further execution necessary. */ return; break; case 'rss': if(isset($_GET['tags'])) { $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']); } if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) { $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00"); } if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) { $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59"); } $this->getRSSFeed(); return; break; } } /* if date-search variables are registered in the session, set the check for "consider date-range" in the html output */ if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) $this->tmpl->assign('date_search_enabled', true); /* if rate-search variables are registered in the session, set the check for "consider rate-range" in the html output */ if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) { $this->tmpl->assign('rate_search_enabled', true); } $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false); $this->tmpl->assign('search_from_date', $this->get_date_text_field('from')); $this->tmpl->assign('search_to_date', $this->get_date_text_field('to')); $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags()); $this->tmpl->assign('preset_available_tags', $this->getAvailableTags()); $this->tmpl->assign('rate_search', $this->get_rate_search()); /* if no site-content has been set yet... */ if(!isset($content)) { /* if tags are already selected, we can immediately display photo-index */ if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) && isset($_SESSION['start_action']) && $_SESSION['start_action'] != 'showp') || (isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi')) $this->tmpl->assign('initial_content', $this->showPhotoIndex()); else { /* if a photo is already selected, we can immediately display single-photo */ if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo'])) $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo'])); else { /* ok, then let us show the welcome page... */ $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl')); } } } else $this->tmpl->assign('initial_content', $content); $this->tmpl->show("index.tpl"); } // show() /** * get_tags - grab all tags of f-spot's database * * this function will get all available tags from * the f-spot database and store them within two * arrays within this class for later usage. in * fact, if the user requests (hide_tags) it will * opt-out some of them. * * this function is getting called once by show() */ private function get_tags() { $this->avail_tags = Array(); $count = 0; /* if show_tags has been set in the configuration (only show photos which are tagged by these tags) they following will take care, that only these other tags are displayed where the photo is also tagged with one of show_tags. */ if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) { $query_str=" SELECT DISTINCT t1.id as id, t1.name as name FROM photo_tags pt1 INNER JOIN photo_tags pt2 ON pt1.photo_id=pt2.photo_id INNER JOIN tags t1 ON t1.id=pt1.tag_id INNER JOIN tags t2 ON t2.id=pt2.tag_id WHERE t2.name IN ('".implode("','",$this->cfg->show_tags)."') ORDER BY t1.sort_priority ASC"; $result = $this->db->db_query($query_str); } else { $result = $this->db->db_query(" SELECT id as id,name as name FROM tags ORDER BY sort_priority ASC "); } while($row = $this->db->db_fetch_object($result)) { $tag_id = $row['id']; $tag_name = $row['name']; /* if the user has specified to ignore this tag in phpfspot's configuration, ignore it here so it does not get added to the tag list. */ if(in_array($row['name'], $this->cfg->hide_tags)) continue; /* if you include the following if-clause and the user has specified to only show certain tags which are specified in phpfspot's configuration, ignore all others so they will not be added to the tag list. if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) && !in_array($row['name'], $this->cfg->show_tags)) continue; */ $this->tags[$tag_id] = $tag_name; $this->avail_tags[$count] = $tag_id; $count++; } } // get_tags() /** * get all photo details from F-Spot database * * this function queries the F-Spot database for all available * details of the requested photo. It returns them as a object. * * Furthermore it takes care of the photo version to be requested. * If photo version is not yet, it queries information for the * original photo. * * @param integer $idx * @return object|null */ public function get_photo_details($idx, $version_idx = NULL) { /* ~ F-Spot version 0.3.x */ if($this->dbver < 9) { $query_str = " SELECT p.id as id, p.name as name, p.time as time, p.directory_path as directory_path, p.description as description FROM photos p "; } else { /* till F-Spot version 0.4.1 */ if($this->dbver < 11) { $query_str = " SELECT p.id as id, p.uri as uri, p.time as time, p.description as description FROM photos p "; } elseif($this->dbver < 17) { /* rating value got introduced */ $query_str = " SELECT p.id as id, p.uri as uri, p.time as time, p.description as description, p.rating as rating FROM photos p "; } else { /* path & filename now splited in base_uri & filename */ $query_str = " SELECT p.id as id, p.base_uri || p.filename as uri, p.time as time, p.description as description, p.rating as rating FROM photos p "; } } /* if show_tags is set, only return details of photos which are tagged with a tag that has been specified to be shown. */ if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) { $query_str.= " INNER JOIN photo_tags pt ON p.id=pt.photo_id INNER JOIN tags t ON pt.tag_id=t.id WHERE p.id='". $idx ."' AND t.name IN ('".implode("','",$this->cfg->show_tags)."')"; } else { $query_str.= " WHERE p.id='". $idx ."' "; } if($row = $this->db->db_fetchSingleRow($query_str)) { /* before F-Spot db version 9 there was no uri column but seperated fields for directory_path and name (= filename). */ if($this->dbver < 9) { $row['uri'] = "file://". $row['directory_path'] ."/". $row['name']; } /* starting with dbversion >= 17 we need to rawurldecode() uri */ elseif($this->dbver >= 17) { $row['uri'] = rawurldecode($row['uri']); } /* if version-idx has not yet been set, get the latest photo version */ if(!isset($version_idx) || !$this->is_valid_version($idx, $version_idx)) $version_idx = $this->get_latest_version($idx); /* if an alternative version has been requested. But we support this only for F-Spot database versions from v9. */ if($version_idx > 0 && $this->dbver >= 9) { if ($this->dbver < 17) { /* check for alternative versions */ if($version = $this->db->db_fetchSingleRow(" SELECT version_id, name, uri FROM photo_versions WHERE photo_id LIKE '". $idx ."' AND version_id LIKE '". $version_idx ."'")) { $row['name'] = $version['name']; $row['uri'] = $version['uri']; } } else { /* path & filename now splited in base_uri & filename */ if($version = $this->db->db_fetchSingleRow(" SELECT version_id, name, base_uri || filename as uri FROM photo_versions WHERE photo_id LIKE '". $idx ."' AND version_id LIKE '". $version_idx ."'")) { $row['name'] = $version['name']; $row['uri'] = rawurldecode($version['uri']); } } } return $row; } return null; } // get_photo_details() /** * returns aligned photo names * * this function returns aligned (length) names for a specific photo. * If the length of the name exceeds $limit the name will bei * shrinked (...). * * @param integer $idx * @param integer $limit * @return string|null */ public function getPhotoName($idx, $limit = 0) { if($details = $this->get_photo_details($idx)) { if($long_name = $this->parse_uri($details['uri'], 'filename')) { $name = $this->shrink_text($long_name, $limit); return $name; } } return null; } // getPhotoName() /** * get photo rating level * * this function will return the integer-based rating level of a * photo. This can only be done, if the F-Spot database is at a * specific version. If rating value can not be found, zero will * be returned indicating no rating value is available. * * @param integer idx * @return integer */ public function get_photo_rating($idx) { if($detail = $this->get_photo_details($idx)) { if(isset($detail['rating'])) return $detail['rating']; } return 0; } // get_photo_rating() /** * get rate-search bars * * this function will return the rating-bars for the search field. * * @return string */ public function get_rate_search() { $bar = ""; for($i = 1; $i <= 5; $i++) { $bar.= "cfg->web_path ."/resources/star.png"; else $bar.= $this->cfg->web_path ."/resources/empty_rate.png"; $bar.= "\" onmouseover=\"show_rate('from', ". $i .");\" onmouseout=\"reset_rate('from');\" onclick=\"set_rate('from', ". $i .")\" />"; } $bar.= "
\n"; for($i = 1; $i <= 5; $i++) { $bar.= "cfg->web_path ."/resources/star.png"; else $bar.= $this->cfg->web_path ."/resources/empty_rate.png"; $bar.= "\" onmouseover=\"show_rate('to', ". $i .");\" onmouseout=\"reset_rate('to');\" onclick=\"set_rate('to', ". $i .");\" />"; } return $bar; } // get_rate_search() /** * shrink text according provided limit * * If the length of the name exceeds $limit, text will be shortend * and inner content will be replaced with "...". * * @param string $ext * @param integer $limit * @return string */ private function shrink_text($text, $limit) { if($limit != 0 && strlen($text) > $limit) { $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5)); } return $text; } // shrink_text(); /** * translate f-spoth photo path * * as the full-qualified path recorded in the f-spot database * is usally not the same as on the webserver, this function * will replace the path with that one specified in the cfg * @param string $path * @return string */ public function translate_path($path) { if($this->cfg->enable_replace_path == true) return str_replace( $this->cfg->path_replace_from, $this->cfg->path_replace_to, $path); return $path; } // translate_path /** * control HTML ouput for a single photo * * this function provides all the necessary information * for the single photo template. * @param integer photo */ public function showPhoto($photo) { /* get all photos from the current photo selection */ $all_photos = $this->getPhotoSelection(); $count = count($all_photos); for($i = 0; $i < $count; $i++) { // $get_next will be set, when the photo which has to // be displayed has been found - this means that the // next available is in fact the NEXT image (for the // navigation icons) if(isset($get_next)) { $next_img = $all_photos[$i]; break; } /* the next photo is our NEXT photo */ if($all_photos[$i] == $photo) { $get_next = 1; } else { $previous_img = $all_photos[$i]; } if($photo == $all_photos[$i]) { $current = $i; } } $details = $this->get_photo_details($photo); if(!$details) { print "error"; return; } $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath')); /* if current version is already set, use it */ if($this->get_current_version() !== false) $version = $this->get_current_version(); /* if version not set yet, we assume to display the latest version */ if(!isset($version) || !$this->is_valid_version($photo, $version)) $version = $this->get_latest_version($photo); $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo, $version); if(!file_exists($orig_path)) { $this->_error("Photo ". $orig_path ." does not exist!
\n"); return; } if(!is_readable($orig_path)) { $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."
\n"); return; } /* If the thumbnail doesn't exist yet, try to create it */ if(!file_exists($thumb_path)) { $this->gen_thumb($photo, true); $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo, $version); } /* get mime-type, height and width from the original photo */ $info = getimagesize($orig_path); /* get EXIF information if JPEG */ if(isset($info['mime']) && $info['mime'] == "image/jpeg") { $meta = $this->get_meta_informations($orig_path); } /* If EXIF data are available, use them */ if(isset($meta['ExifImageWidth'])) { $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength']; } else { $meta_res = $info[0] ."x". $info[1]; } $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a"; $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a"; $extern_link = "index.php?mode=showp&id=". $photo; $current_tags = $this->getCurrentTags(); if($current_tags != "") { $extern_link.= "&tags=". $current_tags; } if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) { $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']); } $this->tmpl->assign('extern_link', $extern_link); if(!file_exists($thumb_path)) { $this->_error("Can't open file ". $thumb_path ."\n"); return; } $info_thumb = getimagesize($thumb_path); $this->tmpl->assign('description', $details['description']); $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename')); $this->tmpl->assign('image_rating', $this->get_photo_rating($photo)); $this->tmpl->assign('width', $info_thumb[0]); $this->tmpl->assign('height', $info_thumb[1]); $this->tmpl->assign('ExifMadeOn', strftime("%a %x %X", $details['time'])); $this->tmpl->assign('ExifMadeWith', $meta_make); $this->tmpl->assign('ExifOrigResolution', $meta_res); $this->tmpl->assign('ExifFileSize', $meta_size); if($this->is_user_friendly_url()) { $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width .'/'. $version); $this->tmpl->assign('image_url_full', '/photo/'. $photo); } else { $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width ."&version=". $version); $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo); } $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename')); $this->tmpl->assign('tags', $this->get_photo_tags($photo)); $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count)); $this->tmpl->assign('current_img', $photo); if(isset($previous_img)) { $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");"); $this->tmpl->assign('prev_img', $previous_img); } if(isset($next_img)) { $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");"); $this->tmpl->assign('next_img', $next_img); } $this->tmpl->assign('mini_width', $this->cfg->mini_width); $this->tmpl->assign('photo_width', $this->cfg->photo_width); $this->tmpl->assign('photo_number', $i); $this->tmpl->assign('photo_count', count($all_photos)); $this->tmpl->assign('photo', $photo); $this->tmpl->assign('version', $version); /* if the photo as alternative versions, set a flag for the template */ if($this->get_photo_versions($photo)) $this->tmpl->assign('has_versions', true); $this->tmpl->register_function("photo_version_select_list", array(&$this, "smarty_photo_version_select_list"), false); return $this->tmpl->fetch("single_photo.tpl"); } // showPhoto() /** * all available tags and tag cloud * * this function outputs all available tags (time ordered) * and in addition output them as tag cloud (tags which have * many photos will appears more then others) */ public function getAvailableTags() { /* retrive tags from database */ $this->get_tags(); $output = ""; $result = $this->db->db_query(" SELECT tag_id as id, count(tag_id) as quantity FROM photo_tags INNER JOIN tags t ON t.id = tag_id GROUP BY tag_id ORDER BY t.name ASC "); $tags = Array(); while($row = $this->db->db_fetch_object($result)) { $tags[$row['id']] = $row['quantity']; } // change these font sizes if you will $max_size = 125; // max font size in % $min_size = 75; // min font size in % // color $max_sat = hexdec('cc'); $min_sat = hexdec('44'); // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); $step_sat = ($max_sat - $min_sat)/($spread); // loop through our tag array foreach ($tags as $key => $value) { /* has the currently processed tag already been added to the selected tag list? if so, ignore it here... */ if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags'])) continue; // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: $size = ceil($size); $color = $min_sat + ($value - $min_qty) * $step_sat; $r = '44'; $g = dechex($color); $b = '88'; if(isset($this->tags[$key])) { if($this->is_user_friendly_url()) { $output.= "cfg->web_path ."/tag/". $key ."\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\" title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] .", "; } else { $output.= "cfg->web_path ."/index.php?mode=showpi\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\" title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] .", "; } } } $output = substr($output, 0, strlen($output)-2); return $output; } // getAvailableTags() /** * output all selected tags * * this function output all tags which have been selected * by the user. the selected tags are stored in the * session-variable $_SESSION['selected_tags'] * @return string */ public function getSelectedTags($type = 'link') { /* retrive tags from database */ $this->get_tags(); $output = ""; foreach($this->avail_tags as $tag) { // return all selected tags if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) { switch($type) { default: case 'link': $output.= "". $this->tags[$tag] .", "; break; case 'img': $output.= "
tags[$tag] ."\"> cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
"; break; } } } if($output != "") { $output = substr($output, 0, strlen($output)-2); return $output; } else { return "no tags selected"; } } // getSelectedTags() /** * add tag to users session variable * * this function will add the specified to users current * tag selection. if a date search has been made before * it will be now cleared * @return string */ public function addTag($tag) { if(!isset($_SESSION['selected_tags'])) $_SESSION['selected_tags'] = Array(); if(isset($_SESSION['searchfor_tag'])) unset($_SESSION['searchfor_tag']); // has the user requested to hide this tag, and still someone, // somehow tries to add it, don't allow this. if(!isset($this->cfg->hide_tags) && in_array($this->get_tag_name($tag), $this->cfg->hide_tags)) return "ok"; if(!in_array($tag, $_SESSION['selected_tags'])) array_push($_SESSION['selected_tags'], $tag); return "ok"; } // addTag() /** * remove tag to users session variable * * this function removes the specified tag from * users current tag selection * @param string $tag * @return string */ public function delTag($tag) { if(isset($_SESSION['searchfor_tag'])) unset($_SESSION['searchfor_tag']); if(isset($_SESSION['selected_tags'])) { $key = array_search($tag, $_SESSION['selected_tags']); unset($_SESSION['selected_tags'][$key]); sort($_SESSION['selected_tags']); } return "ok"; } // delTag() /** * reset tag selection * * if there is any tag selection, it will be * deleted now */ public function resetTags() { if(isset($_SESSION['selected_tags'])) unset($_SESSION['selected_tags']); } // resetTags() /** * returns the value for the autocomplete tag-search * @return string */ public function get_xml_tag_list() { if(!isset($_GET['search']) || !is_string($_GET['search'])) $_GET['search'] = ''; $length = 15; $i = 1; /* retrive tags from database */ $this->get_tags(); $matched_tags = Array(); header("Content-Type: text/xml"); $string = "\n"; $string.= "\n"; foreach($this->avail_tags as $tag) { if(!empty($_GET['search']) && preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) && count($matched_tags) < $length) { $count = $this->get_num_photos($tag); if($count == 1) { $string.= " ". $this->tags[$tag] ."\n"; } else { $string.= " ". $this->tags[$tag] ."\n"; } $i++; } /* if we have collected enough items, break out */ if(count($matched_tags) >= $length) break; } $string.= "\n"; return $string; } // get_xml_tag_list() /** * reset single photo * * if a specific photo was requested (external link) * unset the session variable now */ public function resetPhotoView() { if(isset($_SESSION['current_photo'])) unset($_SESSION['current_photo']); if(isset($_SESSION['current_version'])) unset($_SESSION['current_version']); } // resetPhotoView(); /** * reset tag search * * if any tag search has taken place, reset it now */ public function resetTagSearch() { if(isset($_SESSION['searchfor_tag'])) unset($_SESSION['searchfor_tag']); } // resetTagSearch() /** * reset name search * * if any name search has taken place, reset it now */ public function resetNameSearch() { if(isset($_SESSION['searchfor_name'])) unset($_SESSION['searchfor_name']); } // resetNameSearch() /** * reset date search * * if any date search has taken place, reset it now. */ public function resetDateSearch() { if(isset($_SESSION['from_date'])) unset($_SESSION['from_date']); if(isset($_SESSION['to_date'])) unset($_SESSION['to_date']); } // resetDateSearch(); /** * reset rate search * * if any rate search has taken place, reset it now. */ public function resetRateSearch() { if(isset($_SESSION['rate_from'])) unset($_SESSION['rate_from']); if(isset($_SESSION['rate_to'])) unset($_SESSION['rate_to']); } // resetRateSearch(); /** * return all photo according selection * * this function returns all photos based on * the tag-selection, tag- or date-search. * the tag-search also has to take care of AND * and OR conjunctions * @return array */ public function getPhotoSelection() { $matched_photos = Array(); $additional_where_cond = ""; if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) { $from_date = $_SESSION['from_date']; $to_date = $_SESSION['to_date']; $additional_where_cond.= " p.time>='". $from_date ."' AND p.time<='". $to_date ."' "; } if(isset($_SESSION['searchfor_name'])) { /* check for previous conditions. if so add 'AND' */ if(!empty($additional_where_cond)) { $additional_where_cond.= " AND "; } if($this->dbver < 9) { $additional_where_cond.= " ( p.name LIKE '%". $_SESSION['searchfor_name'] ."%' OR p.description LIKE '%". $_SESSION['searchfor_name'] ."%' ) "; } if($this->dbver < 17) { $additional_where_cond.= " ( basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%' OR p.description LIKE '%". $_SESSION['searchfor_name'] ."%' ) "; } else { $additional_where_cond.= " ( p.filename LIKE '%". $_SESSION['searchfor_name'] ."%' OR p.description LIKE '%". $_SESSION['searchfor_name'] ."%' ) "; } } /* limit result based on rate-search */ if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) { if($this->dbver > 10) { /* check for previous conditions. if so add 'AND' */ if(!empty($additional_where_cond)) { $additional_where_cond.= " AND "; } $additional_where_cond.= " p.rating >= ". $_SESSION['rate_from'] ." AND p.rating <= ". $_SESSION['rate_to'] ." "; } } if(isset($_SESSION['sort_order'])) { $order_str = $this->get_sort_order(); } /* return a search result */ if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') { $query_str = " SELECT DISTINCT pt1.photo_id as photo_id FROM photo_tags pt1 INNER JOIN photo_tags pt2 ON pt1.photo_id=pt2.photo_id INNER JOIN tags t ON pt1.tag_id=t.id INNER JOIN photos p ON pt1.photo_id=p.id INNER JOIN tags t2 ON pt2.tag_id=t2.id WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' "; if(!empty($additional_where_cond)) $query_str.= "AND ". $additional_where_cond ." "; if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) { $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')"; } if(isset($order_str)) $query_str.= $order_str; $result = $this->db->db_query($query_str); while($row = $this->db->db_fetch_object($result)) { array_push($matched_photos, $row['photo_id']); } return $matched_photos; } /* return according the selected tags */ if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) { $selected = ""; foreach($_SESSION['selected_tags'] as $tag) $selected.= $tag .","; $selected = substr($selected, 0, strlen($selected)-1); /* photo has to match at least on of the selected tags */ if($_SESSION['tag_condition'] == 'or') { $query_str = " SELECT DISTINCT pt1.photo_id as photo_id FROM photo_tags pt1 INNER JOIN photo_tags pt2 ON pt1.photo_id=pt2.photo_id INNER JOIN tags t ON pt2.tag_id=t.id INNER JOIN photos p ON pt1.photo_id=p.id WHERE pt1.tag_id IN (". $selected .") "; if(!empty($additional_where_cond)) $query_str.= "AND ". $additional_where_cond ." "; if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) { $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')"; } if(isset($order_str)) $query_str.= $order_str; } /* photo has to match all selected tags */ elseif($_SESSION['tag_condition'] == 'and') { if(count($_SESSION['selected_tags']) >= 32) { print "A SQLite limit of 32 tables within a JOIN SELECT avoids to
\n"; print "evaluate your tag selection. Please remove some tags from your selection.\n"; return Array(); } /* Join together a table looking like pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ... so the query can quickly return all images matching the selected tags in an AND condition */ $query_str = " SELECT DISTINCT pt1.photo_id as photo_id FROM photo_tags pt1 "; if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) { $query_str.= " INNER JOIN tags t ON pt1.tag_id=t.id "; } for($i = 0; $i < count($_SESSION['selected_tags']); $i++) { $query_str.= " INNER JOIN photo_tags pt". ($i+2) ." ON pt1.photo_id=pt". ($i+2) .".photo_id "; } $query_str.= " INNER JOIN photos p ON pt1.photo_id=p.id "; $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." "; for($i = 1; $i < count($_SESSION['selected_tags']); $i++) { $query_str.= " AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ." "; } if(!empty($additional_where_cond)) $query_str.= "AND ". $additional_where_cond; if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) { $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')"; } if(isset($order_str)) $query_str.= $order_str; } $result = $this->db->db_query($query_str); while($row = $this->db->db_fetch_object($result)) { array_push($matched_photos, $row['photo_id']); } return $matched_photos; } /* return all available photos */ $query_str = " SELECT DISTINCT p.id as id FROM photos p LEFT JOIN photo_tags pt ON p.id=pt.photo_id LEFT JOIN tags t ON pt.tag_id=t.id "; if(!empty($additional_where_cond)) $query_str.= "WHERE ". $additional_where_cond ." "; if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) { if(!empty($additional_where_cond)) $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')"; else $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')"; } if(isset($order_str)) $query_str.= $order_str; $result = $this->db->db_query($query_str); while($row = $this->db->db_fetch_object($result)) { array_push($matched_photos, $row['id']); } return $matched_photos; } // getPhotoSelection() /** * control HTML ouput for photo index * * this function provides all the necessary information * for the photo index template. * @return string */ public function showPhotoIndex() { $photos = $this->getPhotoSelection(); $current_tags = $this->getCurrentTags(); $count = count($photos); /* if all thumbnails should be shown on one page */ if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) { $begin_with = 0; $end_with = $count; } /* thumbnails should be splitted up in several pages */ elseif($this->cfg->thumbs_per_page > 0) { if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) { $begin_with = 0; } else { $begin_with = $_SESSION['begin_with']; } $end_with = $begin_with + $this->cfg->thumbs_per_page; } $thumbs = 0; for($i = $begin_with; $i < $end_with; $i++) { if(!isset($photos[$i])) continue; /* on first run, initalize all used variables */ if($thumbs == 0) { $images = Array(); $images[$thumbs] = Array(); $img_height[$thumbs] = Array(); $img_width[$thumbs] = Array(); $img_id[$thumbs] = Array(); $img_name[$thumbs] = Array(); $img_fullname[$thumbs] = Array(); $img_title = Array(); $img_rating = Array(); } $images[$thumbs] = $photos[$i]; $img_id[$thumbs] = $i; $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15)); $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0)); $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0)); $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]); /* get local path of the thumbnail image to be displayed */ $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i], $this->get_latest_version($photos[$i])); /* if the image exist and is readable, extract some details */ if(file_exists($thumb_path) && is_readable($thumb_path)) { if($info = getimagesize($thumb_path) !== false) { $img_width[$thumbs] = $info[0]; $img_height[$thumbs] = $info[1]; } } $thumbs++; } if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']); if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) { $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date'])); $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date'])); } if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) { $this->tmpl->assign('tag_result', 1); } /* do we have to display the page selector ? */ if($this->cfg->thumbs_per_page != 0) { $page_select = ""; /* calculate the page switchers */ $previous_start = $begin_with - $this->cfg->thumbs_per_page; $next_start = $begin_with + $this->cfg->thumbs_per_page; if($begin_with != 0) $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");"); if($end_with < $count) $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");"); $photo_per_page = $this->cfg->thumbs_per_page; $last_page = ceil($count / $photo_per_page); /* get the current selected page */ if($begin_with == 0) { $current_page = 1; } else { $current_page = 0; for($i = $begin_with; $i >= 0; $i-=$photo_per_page) { $current_page++; } } $dotdot_made = 0; for($i = 1; $i <= $last_page; $i++) { if($current_page == $i) $style = "style=\"font-size: 125%; text-decoration: underline;\""; elseif($current_page-1 == $i || $current_page+1 == $i) $style = "style=\"font-size: 105%;\""; elseif(($current_page-5 >= $i) && ($i != 1) || ($current_page+5 <= $i) && ($i != $last_page)) $style = "style=\"font-size: 75%;\""; else $style = ""; $start_with = ($i*$photo_per_page)-$photo_per_page; if($this->is_user_friendly_url()) { $select = "cfg->web_path ."/tag/205/". $start_with ."\""; } else { $select = "cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\""; } $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\""; if($style != "") $select.= $style; $select.= ">". $i ." "; // until 9 pages we show the selector from 1-9 if($last_page <= 9) { $page_select.= $select; continue; } else { if($i == 1 /* first page */ || $i == $last_page /* last page */ || $i == $current_page /* current page */ || $i == ceil($last_page * 0.25) /* first quater */ || $i == ceil($last_page * 0.5) /* half */ || $i == ceil($last_page * 0.75) /* third quater */ || (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ || (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 */ || $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ || $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) { $page_select.= $select; $dotdot_made = 0; continue; } } if(!$dotdot_made) { $page_select.= "......... "; $dotdot_made = 1; } } /* only show the page selector if we have more then one page */ if($last_page > 1) $this->tmpl->assign('page_selector', $page_select); } $extern_link = "index.php?mode=showpi"; $rss_link = "index.php?mode=rss"; if($current_tags != "") { $extern_link.= "&tags=". $current_tags; $rss_link.= "&tags=". $current_tags; } if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) { $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']); $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']); } $export_link = "index.php?mode=export"; $slideshow_link = "index.php?mode=slideshow"; $this->tmpl->assign('extern_link', $extern_link); $this->tmpl->assign('slideshow_link', $slideshow_link); $this->tmpl->assign('export_link', $export_link); $this->tmpl->assign('rss_link', $rss_link); $this->tmpl->assign('count', $count); $this->tmpl->assign('width', $this->cfg->thumb_width); $this->tmpl->assign('preview_width', $this->cfg->photo_width); $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width); $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20); $this->tmpl->assign('selected_tags', $this->getSelectedTags('img')); // +1 for for smarty's selection iteration $this->tmpl->assign('thumbs', $thumbs+1); if($thumbs > 0) { $this->tmpl->assign('images', $images); $this->tmpl->assign('img_width', $img_width); $this->tmpl->assign('img_height', $img_height); $this->tmpl->assign('img_id', $img_id); $this->tmpl->assign('img_name', $img_name); $this->tmpl->assign('img_fullname', $img_fullname); $this->tmpl->assign('img_title', $img_title); $this->tmpl->assign('img_rating', $img_rating); } $result = $this->tmpl->fetch("photo_index.tpl"); /* if we are returning to photo index from an photo-view, scroll the window to the last shown photo-thumbnail. after this, unset the last_photo session variable. */ if(isset($_SESSION['last_photo'])) { $result.= "\n"; unset($_SESSION['last_photo']); } return $result; } // showPhotoIndex() /** * show credit template */ public function showCredits() { $this->tmpl->assign('version', $this->cfg->version); $this->tmpl->assign('product', $this->cfg->product); $this->tmpl->assign('db_version', $this->dbver); $this->tmpl->show("credits.tpl"); } // showCredits() /** * create thumbnails for the requested width * * this function creates image thumbnails of $orig_image * stored as $thumb_image. It will check if the image is * in a supported format, if necessary rotate the image * (based on EXIF orientation meta headers) and re-sizing. * @param string $orig_image * @param string $thumb_image * @param integer $width * @return boolean */ public function create_thumbnail($orig_image, $thumb_image, $width) { if(!file_exists($orig_image)) { return false; } $mime = $this->get_mime_info($orig_image); /* check if original photo is a support image type */ if(!$this->checkifImageSupported($mime)) return false; switch($mime) { case 'image/jpeg': $meta = $this->get_meta_informations($orig_image); $rotate = 0; $flip_hori = false; $flip_vert = false; if(isset($meta['Orientation'])) { switch($meta['Orientation']) { case 1: /* top, left */ /* nothing to do */ break; case 2: /* top, right */ $rotate = 0; $flip_hori = true; break; case 3: /* bottom, left */ $rotate = 180; break; case 4: /* bottom, right */ $flip_vert = true; break; case 5: /* left side, top */ $rotate = 90; $flip_vert = true; break; case 6: /* right side, top */ $rotate = 90; break; case 7: /* left side, bottom */ $rotate = 270; $flip_vert = true; break; case 8: /* right side, bottom */ $rotate = 270; break; } } $src_img = @imagecreatefromjpeg($orig_image); $handler = "gd"; break; case 'image/png': $src_img = @imagecreatefrompng($orig_image); $handler = "gd"; break; case 'image/x-portable-pixmap': $src_img = new Imagick($orig_image); $handler = "imagick"; break; } if(!isset($src_img) || empty($src_img)) { print "Can't load image from ". $orig_image ."\n"; return false; } switch($handler) { case 'gd': /* grabs the height and width */ $cur_width = imagesx($src_img); $cur_height = imagesy($src_img); // If requested width is more then the actual image width, // do not generate a thumbnail, instead safe the original // as thumbnail but with lower quality. But if the image // is to heigh too, then we still have to resize it. if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) { $result = imagejpeg($src_img, $thumb_image, 75); imagedestroy($src_img); return true; } break; case 'imagick': $cur_width = $src_img->getImageWidth(); $cur_height = $src_img->getImageHeight(); // If requested width is more then the actual image width, // do not generate a thumbnail, instead safe the original // as thumbnail but with lower quality. But if the image // is to heigh too, then we still have to resize it. if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) { $src_img->setCompressionQuality(75); $src_img->setImageFormat('jpeg'); $src_img->writeImage($thumb_image); $src_img->clear(); $src_img->destroy(); return true; } break; } // If the image will be rotate because EXIF orientation said so // 'virtually rotate' the image for further calculations if($rotate == 90 || $rotate == 270) { $tmp = $cur_width; $cur_width = $cur_height; $cur_height = $tmp; } /* calculates aspect ratio */ $aspect_ratio = $cur_height / $cur_width; /* sets new size */ if($aspect_ratio < 1) { $new_w = $width; $new_h = abs($new_w * $aspect_ratio); } else { /* 'virtually' rotate the image and calculate it's ratio */ $tmp_w = $cur_height; $tmp_h = $cur_width; /* now get the ratio from the 'rotated' image */ $tmp_ratio = $tmp_h/$tmp_w; /* now calculate the new dimensions */ $tmp_w = $width; $tmp_h = abs($tmp_w * $tmp_ratio); // now that we know, how high they photo should be, if it // gets rotated, use this high to scale the image $new_h = $tmp_h; $new_w = abs($new_h / $aspect_ratio); // If the image will be rotate because EXIF orientation said so // now 'virtually rotate' back the image for the image manipulation if($rotate == 90 || $rotate == 270) { $tmp = $new_w; $new_w = $new_h; $new_h = $tmp; } } switch($handler) { case 'gd': /* creates new image of that size */ $dst_img = imagecreatetruecolor($new_w, $new_h); imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255)); /* copies resized portion of original image into new image */ imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img)); /* needs the image to be flipped horizontal? */ if($flip_hori) { $this->_debug("(FLIP)"); $dst_img = $this->flipImage($dst_img, 'hori'); } /* needs the image to be flipped vertical? */ if($flip_vert) { $this->_debug("(FLIP)"); $dst_img = $this->flipImage($dst_img, 'vert'); } if($rotate) { $this->_debug("(ROTATE)"); $dst_img = $this->rotateImage($dst_img, $rotate); } /* write down new generated file */ $result = imagejpeg($dst_img, $thumb_image, 75); /* free your mind */ imagedestroy($dst_img); imagedestroy($src_img); if($result === false) { print "Can't write thumbnail ". $thumb_image ."\n"; return false; } return true; break; case 'imagick': $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1); /* needs the image to be flipped horizontal? */ if($flip_hori) { $this->_debug("(FLIP)"); $src_img->rotateImage(new ImagickPixel(), 90); $src_img->flipImage(); $src_img->rotateImage(new ImagickPixel(), -90); } /* needs the image to be flipped vertical? */ if($flip_vert) { $this->_debug("(FLIP)"); $src_img->flipImage(); } if($rotate) { $this->_debug("(ROTATE)"); $src_img->rotateImage(new ImagickPixel(), $rotate); } $src_img->setCompressionQuality(75); $src_img->setImageFormat('jpeg'); if(!$src_img->writeImage($thumb_image)) { print "Can't write thumbnail ". $thumb_image ."\n"; return false; } $src_img->clear(); $src_img->destroy(); return true; break; } } // create_thumbnail() /** * return all exif meta data from the file * @param string $file * @return array */ public function get_meta_informations($file) { return exif_read_data($file); } // get_meta_informations() /** * create phpfspot own sqlite database * * this function creates phpfspots own sqlite database * if it does not exist yet. this own is used to store * some necessary informations (md5 sum's, ...). */ public function check_phpfspot_db() { // if the config table doesn't exist yet, create it if(!$this->cfg_db->db_check_table_exists("images")) { $this->cfg_db->db_exec(" CREATE TABLE images ( img_idx int, img_version_idx int, img_md5 varchar(32), UNIQUE(img_idx, img_version_idx) ) "); } if(!$this->cfg_db->db_check_table_exists("meta")) { $this->cfg_db->db_exec(" CREATE TABLE meta ( meta_key varchar(255), meta_value varchar(255) ) "); /* db_version was added with phpfspot 1.7, before changes on the phpfspot database where not necessary. */ $this->cfg_db->db_exec(" INSERT INTO meta ( meta_key, meta_value ) VALUES ( 'phpfspot Database Version', '". $this->cfg->db_version ."' ) "); } /* if version <= 2 and column img_version_idx does not exist yet */ if($this->get_db_version() <= 2 && !$this->cfg_db->db_check_column_exists("images", "img_version_idx")) { if(!$this->cfg_db->db_start_transaction()) die("Can not start database transaction"); $result = $this->cfg_db->db_exec(" CREATE TEMPORARY TABLE images_temp ( img_idx int, img_version_idx int, img_md5 varchar(32), UNIQUE(img_idx, img_version_idx) ) "); if(!$result) { $this->cfg_db->db_rollback_transaction(); die("Upgrade failed - transaction rollback"); } $result = $this->cfg_db->db_exec(" INSERT INTO images_temp SELECT img_idx, 0, img_md5 FROM images "); if(!$result) { $this->cfg_db->db_rollback_transaction(); die("Upgrade failed - transaction rollback"); } $result = $this->cfg_db->db_exec(" DROP TABLE images "); if(!$result) { $this->cfg_db->db_rollback_transaction(); die("Upgrade failed - transaction rollback"); } $result = $this->cfg_db->db_exec(" CREATE TABLE images ( img_idx int, img_version_idx int, img_md5 varchar(32), UNIQUE(img_idx, img_version_idx) ) "); if(!$result) { $this->cfg_db->db_rollback_transaction(); die("Upgrade failed - transaction rollback"); } $result = $this->cfg_db->db_exec(" INSERT INTO images SELECT * FROM images_temp "); if(!$result) { $this->cfg_db->db_rollback_transaction(); die("Upgrade failed - transaction rollback"); } $result = $this->cfg_db->db_exec(" DROP TABLE images_temp "); if(!$result) { $this->cfg_db->db_rollback_transaction(); die("Upgrade failed - transaction rollback"); } if(!$this->cfg_db->db_commit_transaction()) die("Can not commit database transaction"); } } // check_phpfspot_db /** * generates thumbnails * * This function generates JPEG thumbnails from * provided F-Spot photo indize and its alternative * versions. * * 1. Check if all thumbnail generations (width) are already in place and * readable * 2. Check if the md5sum of the original file has changed * 3. Generate the thumbnails if needed * @param integer $idx * @param integer $force * @param boolean $overwrite */ public function gen_thumb($idx = 0, $force = 0, $overwrite = false) { $error = 0; $versions = Array(0); $resolutions = Array( $this->cfg->thumb_width, $this->cfg->photo_width, $this->cfg->mini_width, 30, ); if($alt_versions = $this->get_photo_versions($idx)) $versions = array_merge($versions, $alt_versions); foreach($versions as $version) { /* get details from F-Spot's database */ $details = $this->get_photo_details($idx, $version); /* calculate file MD5 sum */ $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath')); if(!file_exists($full_path)) { $this->_error("File ". $full_path ." does not exist"); return; } if(!is_readable($full_path)) { $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n"); return; } $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:"); /* If Nikon NEF format, we need to treat it another way */ if(isset($this->cfg->dcraw_bin) && file_exists($this->cfg->dcraw_bin) && is_executable($this->cfg->dcraw_bin) && preg_match('/\.nef$/i', $details['uri'])) { $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path); /* if PPM file does not exist, let dcraw convert it from NEF */ if(!file_exists($ppm_path)) { system($this->cfg->dcraw_bin ." -a ". $full_path); } /* for now we handle the PPM instead of the NEF */ $full_path = $ppm_path; } $file_md5 = md5_file($full_path); $changes = false; foreach($resolutions as $resolution) { $generate_it = false; $thumb_sub_path = substr($file_md5, 0, 2); $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5; /* if thumbnail-subdirectory does not exist yet, create it */ if(!file_exists(dirname($thumb_path))) { mkdir(dirname($thumb_path), 0755); } /* if the thumbnail file doesn't exist, create it */ if(!file_exists($thumb_path) || $force) { $generate_it = true; } elseif($file_md5 != $this->getMD5($idx, $version)) { $generate_it = true; } if($generate_it || $overwrite) { $this->_debug(" ". $resolution ."px"); if(!$this->create_thumbnail($full_path, $thumb_path, $resolution)) $error = 1; $changes = true; } } if(!$changes) { $this->_debug(" already exist"); } /* set the new/changed MD5 sum for the current photo */ if(!$error) { $this->setMD5($idx, $file_md5, $version); } $this->_debug("\n"); } } // gen_thumb() /** * returns stored md5 sum for a specific photo * * this function queries the phpfspot database for a stored MD5 * checksum of the specified photo. It also takes care of the * requested photo version - original or alternative photo. * * @param integer $idx * @return string|null */ public function getMD5($idx, $version_idx = 0) { $result = $this->cfg_db->db_query(" SELECT img_md5 FROM images WHERE img_idx='". $idx ."' AND img_version_idx='". $version_idx ."' "); if(!$result) return NULL; if($img = $this->cfg_db->db_fetch_object($result)) return $img['img_md5']; return NULL; } // getMD5() /** * set MD5 sum for the specific photo * @param integer $idx * @param string $md5 */ private function setMD5($idx, $md5, $version_idx = 0) { $result = $this->cfg_db->db_exec(" INSERT OR REPLACE INTO images ( img_idx, img_version_idx, img_md5 ) VALUES ( '". $idx ."', '". $version_idx ."', '". $md5 ."' ) "); } // setMD5() /** * store current tag condition * * this function stores the current tag condition * (AND or OR) in the users session variables * @param string $mode * @return string */ public function setTagCondition($mode) { $_SESSION['tag_condition'] = $mode; return "ok"; } // setTagCondition() /** * invoke tag & date search * * this function will return all matching tags and store * them in the session variable selected_tags. furthermore * it also handles the date search. * getPhotoSelection() will then only return the matching * photos. * @return string */ public function startSearch() { /* date search */ if(isset($_POST['date_from']) && $this->isValidDate($_POST['date_from'])) { $date_from = $_POST['date_from']; } if(isset($_POST['date_to']) && $this->isValidDate($_POST['date_to'])) { $date_to = $_POST['date_to']; } /* tag-name search */ if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) { $searchfor_tag = $_POST['for_tag']; $_SESSION['searchfor_tag'] = $_POST['for_tag']; } else { unset($_SESSION['searchfor_tag']); } /* file-name search */ if(isset($_POST['for_name']) && is_string($_POST['for_name'])) { $_SESSION['searchfor_name'] = $_POST['for_name']; } else { unset($_SESSION['searchfor_name']); } /* rate-search */ if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) { $_SESSION['rate_from'] = $_POST['rate_from']; if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) { $_SESSION['rate_to'] = $_POST['rate_to']; } } else { /* delete any previously set value */ unset($_SESSION['rate_to'], $_SESSION['rate_from']); } $this->get_tags(); if(isset($date_from) && !empty($date_from)) $_SESSION['from_date'] = strtotime($date_from ." 00:00:00"); else unset($_SESSION['from_date']); if(isset($date_to) && !empty($date_to)) $_SESSION['to_date'] = strtotime($date_to ." 23:59:59"); else unset($_SESSION['to_date']); if(isset($searchfor_tag) && !empty($searchfor_tag)) { /* new search, reset the current selected tags */ $_SESSION['selected_tags'] = Array(); foreach($this->avail_tags as $tag) { if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag])) array_push($_SESSION['selected_tags'], $tag); } } return "ok"; } // startSearch() /** * updates sort order in session variable * * this function is invoked by RPC and will sort the requested * sort order in the session variable. * @param string $sort_order * @return string */ public function updateSortOrder($order) { if(isset($this->sort_orders[$order])) { $_SESSION['sort_order'] = $order; return "ok"; } return "unkown error"; } // updateSortOrder() /** * update photo version in session variable * * this function is invoked by RPC and will set the requested * photo version in the session variable. * @param string $photo_version * @return string */ public function update_photo_version($photo_idx, $photo_version) { if($this->is_valid_version($photo_idx, $photo_version)) { $_SESSION['current_version'] = $photo_version; return "ok"; } return "incorrect photo version provided"; } // update_photo_version() /** * rotate image * * this function rotates the image according the * specified angel. * @param string $img * @param integer $degress * @return image */ private function rotateImage($img, $degrees) { if(function_exists("imagerotate")) { $img = imagerotate($img, $degrees, 0); } else { function imagerotate($src_img, $angle) { $src_x = imagesx($src_img); $src_y = imagesy($src_img); if ($angle == 180) { $dest_x = $src_x; $dest_y = $src_y; } elseif ($src_x <= $src_y) { $dest_x = $src_y; $dest_y = $src_x; } elseif ($src_x >= $src_y) { $dest_x = $src_y; $dest_y = $src_x; } $rotate=imagecreatetruecolor($dest_x,$dest_y); imagealphablending($rotate, false); switch ($angle) { case 90: for ($y = 0; $y < ($src_y); $y++) { for ($x = 0; $x < ($src_x); $x++) { $color = imagecolorat($src_img, $x, $y); imagesetpixel($rotate, $dest_x - $y - 1, $x, $color); } } break; case 270: for ($y = 0; $y < ($src_y); $y++) { for ($x = 0; $x < ($src_x); $x++) { $color = imagecolorat($src_img, $x, $y); imagesetpixel($rotate, $y, $dest_y - $x - 1, $color); } } break; case 180: for ($y = 0; $y < ($src_y); $y++) { for ($x = 0; $x < ($src_x); $x++) { $color = imagecolorat($src_img, $x, $y); imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color); } } break; default: $rotate = $src_img; break; }; return $rotate; } $img = imagerotate($img, $degrees); } return $img; } // rotateImage() /** * returns flipped image * * this function will return an either horizontal or * vertical flipped truecolor image. * @param string $image * @param string $mode * @return image */ private function flipImage($image, $mode) { $w = imagesx($image); $h = imagesy($image); $flipped = imagecreatetruecolor($w, $h); switch($mode) { case 'vert': for ($y = 0; $y < $h; $y++) { imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1); } break; case 'hori': for ($x = 0; $x < $w; $x++) { imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h); } break; } return $flipped; } // flipImage() /** * return all assigned tags for the specified photo * @param integer $idx * @return array */ private function get_photo_tags($idx) { $result = $this->db->db_query(" SELECT t.id as id, t.name as name FROM tags t INNER JOIN photo_tags pt ON t.id=pt.tag_id WHERE pt.photo_id='". $idx ."' "); $tags = Array(); while($row = $this->db->db_fetch_object($result)) { if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags)) continue; $tags[$row['id']] = $row['name']; } return $tags; } // get_photo_tags() /** * create on-the-fly images with text within * @param string $txt * @param string $color * @param integer $space * @param integer $font * @param integer $w */ public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300) { if (strlen($color) != 6) $color = 000000; $int = hexdec($color); $h = imagefontheight($font); $fw = imagefontwidth($font); $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n")); $lines = count($txt); $im = imagecreate($w, (($h * $lines) + ($lines * $space))); $bg = imagecolorallocate($im, 255, 255, 255); $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int); $y = 0; foreach ($txt as $text) { $x = (($w - ($fw * strlen($text))) / 2); imagestring($im, $font, $x, $y, $text, $color); $y += ($h + $space); } Header("Content-type: image/png"); ImagePng($im); } // showTextImage() /** * check if all requirements are met * @return boolean */ private function check_requirements() { if(!function_exists("imagecreatefromjpeg")) { print "PHP GD library extension is missing
\n"; $missing = true; } if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) { print "PHP SQLite3 library extension is missing
\n"; $missing = true; } if($this->cfg->db_access == "pdo") { if(array_search("sqlite", PDO::getAvailableDrivers()) === false) { print "PDO SQLite3 driver is missing
\n"; $missing = true; } } /* Check for HTML_AJAX PEAR package, lent from Horde project */ ini_set('track_errors', 1); @include_once 'HTML/AJAX/Server.php'; if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) { print "PEAR HTML_AJAX package is missing
\n"; $missing = true; } @include_once 'Calendar/Calendar.php'; if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) { print "PEAR Calendar package is missing
\n"; $missing = true; } @include_once 'Console/Getopt.php'; if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) { print "PEAR Console_Getopt package is missing
\n"; $missing = true; } @include_once 'Date.php'; if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) { print "PEAR Date package is missing
\n"; $missing = true; } @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php'; if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) { print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php
\n"; $missing = true; } ini_restore('track_errors'); if(isset($missing)) return false; return true; } // check_requirements() private function _debug($text) { if(isset($this->fromcmd)) { print $text; } } // _debug() /** * check if specified MIME type is supported * @param string $mime * @return boolean */ public function checkifImageSupported($mime) { $supported_types = Array( "image/jpeg", "image/png", "image/x-portable-pixmap", "image/tiff" ); if(in_array($mime, $supported_types)) return true; return false; } // checkifImageSupported() /** * output error text * @param string $text */ public function _error($text) { switch($this->cfg->logging) { default: case 'display': if(isset($this->fromcmd)) print $text ."\n"; else { print "\"warning\"\n"; print $text ."
\n"; } break; case 'errorlog': error_log($text); break; case 'logfile': error_log($text, 3, $this->cfg->log_file); break; } $this->runtime_error = true; } // _error() /** * get calendar input-text fields * * this function returns a text-field used for the data selection. * Either it will be filled with the current date or, if available, * filled with the date user entered previously. * * @param string $mode * @return string */ private function get_date_text_field($mode) { $date = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y"); $date.= "-"; $date.= isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m"); $date.= "-"; $date.= isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d"); $output = "setDate($userdate); $year = $date->getYear(); $month = $date->getMonth(); $day = $date->getDay(); } $rows = 1; $cols = 1; $matrix = Array(); require_once CALENDAR_ROOT.'Month/Weekdays.php'; require_once CALENDAR_ROOT.'Day.php'; // Build the month $month_cal = new Calendar_Month_Weekdays($year,$month); // Create links $prevStamp = $month_cal->prevMonth(true); $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");"; $nextStamp = $month_cal->nextMonth(true); $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");"; $selectedDays = array ( new Calendar_Day($year,$month,$day), ); // Build the days in the month $month_cal->build($selectedDays); $this->tmpl->assign('current_month', date('F Y',$month_cal->getTimeStamp())); $this->tmpl->assign('prev_month', $prev); $this->tmpl->assign('next_month', $next); while ( $day = $month_cal->fetch() ) { if(!isset($matrix[$rows])) $matrix[$rows] = Array(); $string = ""; $dayStamp = $day->thisDay(true); $link = "javascript:setCalendarDate('" . date('Y',$dayStamp) . "-" . date('m',$dayStamp) . "-" . date('d',$dayStamp) ."');"; // isFirst() to find start of week if ( $day->isFirst() ) $string.= "\n"; if ( $day->isSelected() ) { $string.= "".$day->thisDay()."\n"; } else if ( $day->isEmpty() ) { $string.= " \n"; } else { $string.= "".$day->thisDay()."\n"; } // isLast() to find end of week if ( $day->isLast() ) $string.= "\n"; $matrix[$rows][$cols] = $string; $cols++; if($cols > 7) { $cols = 1; $rows++; } } $this->tmpl->assign('matrix', $matrix); $this->tmpl->assign('rows', $rows); $this->tmpl->show("calendar.tpl"); } // get_calendar_matrix() /** * output export page * @param string $mode */ public function getExport($mode) { $pictures = $this->getPhotoSelection(); $current_tags = $this->getCurrentTags(); foreach($pictures as $picture) { $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture; if($current_tags != "") { $orig_url.= "&tags=". $current_tags; } if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) { $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date']; } if($this->is_user_friendly_url()) { $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width; } else { $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width; } switch($mode) { case 'HTML': // print htmlspecialchars("") ."
\n"; break; case 'MoinMoin': // "[%pictureurl% %thumbnailurl%]" print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."
\n"; break; case 'MoinMoinList': // " * [%pictureurl% %thumbnailurl%]" print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."
\n"; break; } } } // getExport() /** * output RSS feed */ public function getRSSFeed() { Header("Content-type: text/xml; charset=utf-8"); print "\n"; ?> phpfspot phpfspot RSS feed get_phpfspot_url()); ?> phpfspot getPhotoSelection(); $current_tags = $this->getCurrentTags(); foreach($pictures as $picture) { $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture; if($current_tags != "") { $orig_url.= "&tags=". $current_tags; } if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) { $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date']; } $details = $this->get_photo_details($picture); if($this->is_user_friendly_url()) { $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width; } else { $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width; } $thumb_html = htmlspecialchars("
". $details['description']); $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath')); /* get EXIF information if JPEG */ if(isset($details['mime']) && $details['mime'] == "image/jpeg") { $meta = $this->get_meta_informations($orig_path); } ?> <?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?>
get_photo_versions($photo_idx)) return $versions[count($versions)-1]; /* if no alternative version were found, return original version */ return 0; } // get_current_version() /** * tells the client browser what to do * * this function is getting called via AJAX by the * client browsers. it will tell them what they have * to do next. This is necessary for directly jumping * into photo index or single photo view when the are * requested with specific URLs * @return string */ public function whatToDo() { if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') { } elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) { return "showpi_tags"; } elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') { return "showpi"; } } // whatToDo() /** * return the current process-user * @return string */ private function getuid() { if($uid = posix_getuid()) { if($user = posix_getpwuid($uid)) { return $user['name']; } } return 'n/a'; } // getuid() /** * photo version select list * * this function returns a HTML select list (drop down) * to select a alternative photo version of the original photo. * * @param array $params * @param smarty $smarty * @return string */ public function smarty_photo_version_select_list($params, &$smarty) { if(!isset($params['photo']) || !isset($params['current'])) return NULL; $output = ""; $versions = $this->get_photo_versions($params['photo']); foreach($versions as $version) { $output.= ""; } return $output; } // smarty_photo_version_select_list() /** * returns a select-dropdown box to select photo index sort parameters * @param array $params * @param smarty $smarty * @return string */ public function smarty_sort_select_list($params, &$smarty) { $output = ""; foreach($this->sort_orders as $key => $value) { $output.= "