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";
31 * this class contains the most functions which will to the major
39 * phpfspot configuration
47 * SQLite database handle to f-spot database
55 * SQLite database handle to phpfspot database
63 * Smarty template engine
64 * @link http://smarty.php.net smarty.php.net
65 * @see PHPFSPOT_TMPL()
79 * list of available, not-selected, tags
86 * true if runtime error occued
90 private $runtime_error = false;
93 * F-Spot database version
100 * class constructor ($cfg, $db, $cfg_db, $tmpl, $db_ver)
102 * this function will be called on class construct
103 * and will check requirements, loads configuration,
104 * open databases and start the user session
106 public function __construct()
109 * register PHPFSPOT class global
111 * @global PHPFSPOT $GLOBALS['phpfspot']
114 $GLOBALS['phpfspot'] =& $this;
116 $this->cfg = new PHPFSPOT_CFG;
118 /* verify config settings */
119 if($this->check_config_options()) {
123 /* set application name and version information */
124 $this->cfg->product = "phpfspot";
125 $this->cfg->version = "1.5";
127 $this->sort_orders= array(
128 'date_asc' => 'Date ↑',
129 'date_desc' => 'Date ↓',
130 'name_asc' => 'Name ↑',
131 'name_desc' => 'Name ↓',
132 'tags_asc' => 'Tags ↑',
133 'tags_desc' => 'Tags ↓',
136 /* Check necessary requirements */
137 if(!$this->check_requirements()) {
141 /******* Opening F-Spot's sqlite database *********/
143 /* Check if database file is writeable */
144 if(!is_writeable($this->cfg->fspot_db)) {
145 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
149 /* open the database */
150 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
152 /* change sqlite temp directory, if requested */
153 if(isset($this->cfg->sqlite_temp_dir)) {
156 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
160 /* get F-Spot database version */
161 $this->dbver = $this->getFspotDBVersion();
163 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
164 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
168 if(!is_writeable($this->cfg->thumb_path)) {
169 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
173 /******* Opening phpfspot's sqlite database *********/
175 /* Check if directory where the database file is stored is writeable */
176 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
177 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
181 /* Check if database file is writeable */
182 if(!is_writeable($this->cfg->phpfspot_db)) {
183 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
187 /* open the database */
188 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
190 /* change sqlite temp directory, if requested */
191 if(isset($this->cfg->sqlite_temp_dir)) {
192 $this->cfg_db->db_exec("
194 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
198 /* Check if some tables need to be created */
199 $this->check_config_table();
201 /* overload Smarty class with our own template handler */
202 require_once "phpfspot_tmpl.php";
203 $this->tmpl = new PHPFSPOT_TMPL();
205 $this->tmpl->assign('web_path', $this->cfg->web_path);
207 /* Starting with F-Spot 0.4.2, the rating-feature was available */
208 if($this->dbver > 10) {
209 $this->tmpl->assign('has_rating', true);
210 $this->sort_orders = array_merge($this->sort_orders, array(
211 'rate_asc' => 'Rate ↑',
212 'rate_desc' => 'Rate ↓',
216 /* check if all necessary indices exist */
217 $this->checkDbIndices();
219 /* if session is not yet started, do it now */
220 if(session_id() == "")
223 if(!isset($_SESSION['tag_condition']))
224 $_SESSION['tag_condition'] = 'or';
226 if(!isset($_SESSION['sort_order']))
227 $_SESSION['sort_order'] = 'date_desc';
229 if(!isset($_SESSION['searchfor_tag']))
230 $_SESSION['searchfor_tag'] = '';
232 // if begin_with is still set but thumbs_per_page is now 0, unset it
233 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
234 unset($_SESSION['begin_with']);
236 // if user-friendly-url's are enabled, set also a flag for the template handler
237 if($this->is_user_friendly_url()) {
238 $this->tmpl->assign('user_friendly_url', 'true');
243 public function __destruct()
249 * show - generate html output
251 * this function can be called after the constructor has
252 * prepared everyhing. it will load the index.tpl smarty
253 * template. if necessary it will registere pre-selects
254 * (photo index, photo, tag search, date search) into
257 public function show()
259 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
260 $this->tmpl->assign('page_title', $this->cfg->page_title);
261 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
262 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
265 if($this->is_user_friendly_url()) {
266 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
269 if(isset($_GET['mode'])) {
271 $_SESSION['start_action'] = $_GET['mode'];
273 switch($_GET['mode']) {
275 if(isset($_GET['tags'])) {
276 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
278 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
279 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
281 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
282 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
286 if(isset($_GET['tags'])) {
287 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
288 $_SESSION['start_action'] = 'showp';
290 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
291 $_SESSION['current_photo'] = $_GET['id'];
292 $_SESSION['start_action'] = 'showp';
294 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
295 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
297 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
298 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
302 $this->tmpl->show("export.tpl");
306 $this->tmpl->show("slideshow.tpl");
310 if(isset($_GET['tags'])) {
311 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
313 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
314 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
316 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
317 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
325 /* if date-search variables are registered in the session, set the check
326 for "consider date-range" in the html output
328 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
329 $this->tmpl->assign('date_search_enabled', true);
331 /* if rate-search variables are registered in the session, set the check
332 for "consider rate-range" in the html output
334 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
335 $this->tmpl->assign('rate_search_enabled', true);
338 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
339 $this->tmpl->assign('search_from_date', $this->get_calendar('from'));
340 $this->tmpl->assign('search_to_date', $this->get_calendar('to'));
342 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
343 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
344 $this->tmpl->assign('rate_search', $this->get_rate_search());
346 if(!isset($content)) {
347 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']))
348 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
350 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
353 $this->tmpl->assign('initial_content', $content);
355 $this->tmpl->show("index.tpl");
360 * get_tags - grab all tags of f-spot's database
362 * this function will get all available tags from
363 * the f-spot database and store them within two
364 * arrays within this class for later usage. in
365 * fact, if the user requests (hide_tags) it will
366 * opt-out some of them.
368 * this function is getting called once by show()
370 private function get_tags()
372 $this->avail_tags = Array();
375 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
378 DISTINCT t1.id as id, t1.name as name
381 INNER JOIN photo_tags
382 pt2 ON pt1.photo_id=pt2.photo_id
388 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
390 t1.sort_priority ASC";
392 $result = $this->db->db_query($query_str);
396 $result = $this->db->db_query("
399 ORDER BY sort_priority ASC
403 while($row = $this->db->db_fetch_object($result)) {
405 $tag_id = $row['id'];
406 $tag_name = $row['name'];
408 /* if the user has specified to ignore this tag in phpfspot's
409 configuration, ignore it here so it does not get added to
412 if(in_array($row['name'], $this->cfg->hide_tags))
415 /* if you include the following if-clause and the user has specified
416 to only show certain tags which are specified in phpfspot's
417 configuration, ignore all others so they will not be added to the
419 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
420 !in_array($row['name'], $this->cfg->show_tags))
424 $this->tags[$tag_id] = $tag_name;
425 $this->avail_tags[$count] = $tag_id;
433 * extract all photo details
435 * retrieve all available details from f-spot's
436 * database and return them as object
437 * @param integer $idx
438 * @return object|null
440 public function get_photo_details($idx)
442 if($this->dbver < 9) {
444 SELECT p.id, p.name, p.time, p.directory_path, p.description
449 /* till F-Spot version 0.4.1 */
450 if($this->dbver < 11) {
452 SELECT p.id, p.uri, p.time, p.description
458 SELECT p.id, p.uri, p.time, p.description, p.rating
464 /* if show_tags is set, only return details for photos which
465 are specified to be shown
467 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
469 INNER JOIN photo_tags pt
473 WHERE p.id='". $idx ."'
474 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
478 WHERE p.id='". $idx ."'
482 if($result = $this->db->db_query($query_str)) {
484 $row = $this->db->db_fetch_object($result);
486 if($this->dbver < 9) {
487 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
496 } // get_photo_details
499 * returns aligned photo names
501 * this function returns aligned (length) names for
502 * an specific photo. If the length of the name exceeds
503 * $limit the name will be shrinked (...)
504 * @param integer $idx
505 * @param integer $limit
506 * @return string|null
508 public function getPhotoName($idx, $limit = 0)
510 if($details = $this->get_photo_details($idx)) {
511 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
512 $name = $this->shrink_text($long_name, $limit);
522 * get photo rating level
524 * this function will return the integer-based rating
525 * level of the photo. This can only be done, if the F-Spot
526 * database is at a specific level. If rating value can not
527 * be found, zero will be returned indicating no rating value
532 public function get_photo_rating($idx)
534 if($detail = $this->get_photo_details($idx)) {
535 if(isset($detail['rating']))
536 return $detail['rating'];
541 } // get_photo_rating()
544 * get rate-search bars
546 * this function will return the rating-bars for the
550 public function get_rate_search()
554 for($i = 1; $i <= 5; $i++) {
556 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
558 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
559 $bar.= $this->cfg->web_path ."/resources/star.png";
561 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
564 onmouseover=\"show_rate('from', ". $i .");\"
565 onmouseout=\"reset_rate('from');\"
566 onclick=\"set_rate('from', ". $i .")\" />";
571 for($i = 1; $i <= 5; $i++) {
573 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
575 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
576 $bar.= $this->cfg->web_path ."/resources/star.png";
578 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
581 onmouseover=\"show_rate('to', ". $i .");\"
582 onmouseout=\"reset_rate('to');\"
583 onclick=\"set_rate('to', ". $i .");\" />";
588 } // get_rate_search()
591 * shrink text according provided limit
593 * If the length of the name exceeds $limit the
594 * text will be shortend and some content in between
595 * will be replaced with "..."
597 * @param integer $limit
600 private function shrink_text($text, $limit)
602 if($limit != 0 && strlen($text) > $limit) {
603 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
611 * translate f-spoth photo path
613 * as the full-qualified path recorded in the f-spot database
614 * is usally not the same as on the webserver, this function
615 * will replace the path with that one specified in the cfg
616 * @param string $path
619 public function translate_path($path)
621 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
626 * control HTML ouput for a single photo
628 * this function provides all the necessary information
629 * for the single photo template.
630 * @param integer photo
632 public function showPhoto($photo)
634 /* get all photos from the current photo selection */
635 $all_photos = $this->getPhotoSelection();
636 $count = count($all_photos);
638 for($i = 0; $i < $count; $i++) {
640 // $get_next will be set, when the photo which has to
641 // be displayed has been found - this means that the
642 // next available is in fact the NEXT image (for the
644 if(isset($get_next)) {
645 $next_img = $all_photos[$i];
649 /* the next photo is our NEXT photo */
650 if($all_photos[$i] == $photo) {
654 $previous_img = $all_photos[$i];
657 if($photo == $all_photos[$i]) {
662 $details = $this->get_photo_details($photo);
669 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
670 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
672 if(!file_exists($orig_path)) {
673 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
677 if(!is_readable($orig_path)) {
678 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
682 /* If the thumbnail doesn't exist yet, try to create it */
683 if(!file_exists($thumb_path)) {
684 $this->gen_thumb($photo, true);
685 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
688 /* get mime-type, height and width from the original photo */
689 $info = getimagesize($orig_path);
691 /* get EXIF information if JPEG */
692 if($info['mime'] == "image/jpeg") {
693 $meta = $this->get_meta_informations($orig_path);
696 /* If EXIF data are available, use them */
697 if(isset($meta['ExifImageWidth'])) {
698 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
700 $meta_res = $info[0] ."x". $info[1];
703 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
704 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
705 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
707 $extern_link = "index.php?mode=showp&id=". $photo;
708 $current_tags = $this->getCurrentTags();
709 if($current_tags != "") {
710 $extern_link.= "&tags=". $current_tags;
712 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
713 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
716 $this->tmpl->assign('extern_link', $extern_link);
718 if(!file_exists($thumb_path)) {
719 $this->_error("Can't open file ". $thumb_path ."\n");
723 $info_thumb = getimagesize($thumb_path);
725 $this->tmpl->assign('description', $details['description']);
726 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
727 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
729 $this->tmpl->assign('width', $info_thumb[0]);
730 $this->tmpl->assign('height', $info_thumb[1]);
731 $this->tmpl->assign('ExifMadeOn', $meta_date);
732 $this->tmpl->assign('ExifMadeWith', $meta_make);
733 $this->tmpl->assign('ExifOrigResolution', $meta_res);
734 $this->tmpl->assign('ExifFileSize', $meta_size);
736 if($this->is_user_friendly_url()) {
737 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
738 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
741 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
742 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
745 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
747 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
748 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
749 $this->tmpl->assign('current_img', $photo);
752 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
753 $this->tmpl->assign('prev_img', $previous_img);
757 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
758 $this->tmpl->assign('next_img', $next_img);
760 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
761 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
762 $this->tmpl->assign('photo_number', $i);
763 $this->tmpl->assign('photo_count', count($all_photos));
765 return $this->tmpl->fetch("single_photo.tpl");
770 * all available tags and tag cloud
772 * this function outputs all available tags (time ordered)
773 * and in addition output them as tag cloud (tags which have
774 * many photos will appears more then others)
776 public function getAvailableTags()
778 /* retrive tags from database */
783 $result = $this->db->db_query("
784 SELECT tag_id as id, count(tag_id) as quantity
794 while($row = $this->db->db_fetch_object($result)) {
795 $tags[$row['id']] = $row['quantity'];
798 // change these font sizes if you will
799 $max_size = 125; // max font size in %
800 $min_size = 75; // min font size in %
803 $max_sat = hexdec('cc');
804 $min_sat = hexdec('44');
806 // get the largest and smallest array values
807 $max_qty = max(array_values($tags));
808 $min_qty = min(array_values($tags));
810 // find the range of values
811 $spread = $max_qty - $min_qty;
812 if (0 == $spread) { // we don't want to divide by zero
816 // determine the font-size increment
817 // this is the increase per tag quantity (times used)
818 $step = ($max_size - $min_size)/($spread);
819 $step_sat = ($max_sat - $min_sat)/($spread);
821 // loop through our tag array
822 foreach ($tags as $key => $value) {
824 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
827 // calculate CSS font-size
828 // find the $value in excess of $min_qty
829 // multiply by the font-size increment ($size)
830 // and add the $min_size set above
831 $size = $min_size + (($value - $min_qty) * $step);
832 // uncomment if you want sizes in whole %:
835 $color = $min_sat + ($value - $min_qty) * $step_sat;
841 if(isset($this->tags[$key])) {
842 if($this->is_user_friendly_url())
843 $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
845 $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
849 $output = substr($output, 0, strlen($output)-2);
852 } // getAvailableTags()
855 * output all selected tags
857 * this function output all tags which have been selected
858 * by the user. the selected tags are stored in the
859 * session-variable $_SESSION['selected_tags']
862 public function getSelectedTags($type = 'link')
864 /* retrive tags from database */
869 foreach($this->avail_tags as $tag)
871 // return all selected tags
872 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
877 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
881 <div class=\"tagresulttag\">
882 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
883 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
893 $output = substr($output, 0, strlen($output)-2);
897 return "no tags selected";
900 } // getSelectedTags()
903 * add tag to users session variable
905 * this function will add the specified to users current
906 * tag selection. if a date search has been made before
907 * it will be now cleared
910 public function addTag($tag)
912 if(!isset($_SESSION['selected_tags']))
913 $_SESSION['selected_tags'] = Array();
915 if(isset($_SESSION['searchfor_tag']))
916 unset($_SESSION['searchfor_tag']);
918 // has the user requested to hide this tag, and still someone,
919 // somehow tries to add it, don't allow this.
920 if(!isset($this->cfg->hide_tags) &&
921 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
924 if(!in_array($tag, $_SESSION['selected_tags']))
925 array_push($_SESSION['selected_tags'], $tag);
932 * remove tag to users session variable
934 * this function removes the specified tag from
935 * users current tag selection
939 public function delTag($tag)
941 if(isset($_SESSION['searchfor_tag']))
942 unset($_SESSION['searchfor_tag']);
944 if(isset($_SESSION['selected_tags'])) {
945 $key = array_search($tag, $_SESSION['selected_tags']);
946 unset($_SESSION['selected_tags'][$key]);
947 sort($_SESSION['selected_tags']);
955 * reset tag selection
957 * if there is any tag selection, it will be
960 public function resetTags()
962 if(isset($_SESSION['selected_tags']))
963 unset($_SESSION['selected_tags']);
968 * returns the value for the autocomplete tag-search
971 public function get_xml_tag_list()
973 if(!isset($_GET['search']) || !is_string($_GET['search']))
974 $_GET['search'] = '';
979 /* retrive tags from database */
982 $matched_tags = Array();
984 header("Content-Type: text/xml");
986 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
987 $string.= "<results>\n";
989 foreach($this->avail_tags as $tag)
991 if(!empty($_GET['search']) &&
992 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
993 count($matched_tags) < $length) {
995 $count = $this->get_num_photos($tag);
998 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1001 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1007 /* if we have collected enough items, break out */
1008 if(count($matched_tags) >= $length)
1012 $string.= "</results>\n";
1016 } // get_xml_tag_list()
1020 * reset single photo
1022 * if a specific photo was requested (external link)
1023 * unset the session variable now
1025 public function resetPhotoView()
1027 if(isset($_SESSION['current_photo']))
1028 unset($_SESSION['current_photo']);
1030 } // resetPhotoView();
1035 * if any tag search has taken place, reset it now
1037 public function resetTagSearch()
1039 if(isset($_SESSION['searchfor_tag']))
1040 unset($_SESSION['searchfor_tag']);
1042 } // resetTagSearch()
1047 * if any name search has taken place, reset it now
1049 public function resetNameSearch()
1051 if(isset($_SESSION['searchfor_name']))
1052 unset($_SESSION['searchfor_name']);
1054 } // resetNameSearch()
1059 * if any date search has taken place, reset it now.
1061 public function resetDateSearch()
1063 if(isset($_SESSION['from_date']))
1064 unset($_SESSION['from_date']);
1065 if(isset($_SESSION['to_date']))
1066 unset($_SESSION['to_date']);
1068 } // resetDateSearch();
1073 * if any rate search has taken place, reset it now.
1075 public function resetRateSearch()
1077 if(isset($_SESSION['rate_from']))
1078 unset($_SESSION['rate_from']);
1079 if(isset($_SESSION['rate_to']))
1080 unset($_SESSION['rate_to']);
1082 } // resetRateSearch();
1085 * return all photo according selection
1087 * this function returns all photos based on
1088 * the tag-selection, tag- or date-search.
1089 * the tag-search also has to take care of AND
1090 * and OR conjunctions
1093 public function getPhotoSelection()
1095 $matched_photos = Array();
1096 $additional_where_cond = "";
1098 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1099 $from_date = $_SESSION['from_date'];
1100 $to_date = $_SESSION['to_date'];
1101 $additional_where_cond.= "
1102 p.time>='". $from_date ."'
1104 p.time<='". $to_date ."'
1108 if(isset($_SESSION['searchfor_name'])) {
1110 /* check for previous conditions. if so add 'AND' */
1111 if(!empty($additional_where_cond)) {
1112 $additional_where_cond.= " AND ";
1115 if($this->dbver < 9) {
1116 $additional_where_cond.= "
1118 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1120 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1125 $additional_where_cond.= "
1127 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1129 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1135 /* limit result based on rate-search */
1136 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1138 if($this->dbver > 10) {
1140 /* check for previous conditions. if so add 'AND' */
1141 if(!empty($additional_where_cond)) {
1142 $additional_where_cond.= " AND ";
1145 $additional_where_cond.= "
1146 p.rating >= ". $_SESSION['rate_from'] ."
1148 p.rating <= ". $_SESSION['rate_to'] ."
1153 if(isset($_SESSION['sort_order'])) {
1154 $order_str = $this->get_sort_order();
1157 /* return a search result */
1158 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1160 SELECT DISTINCT pt1.photo_id
1162 INNER JOIN photo_tags pt2
1163 ON pt1.photo_id=pt2.photo_id
1167 ON pt1.photo_id=p.id
1170 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1172 if(!empty($additional_where_cond))
1173 $query_str.= "AND ". $additional_where_cond ." ";
1175 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1176 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1179 if(isset($order_str))
1180 $query_str.= $order_str;
1182 $result = $this->db->db_query($query_str);
1183 while($row = $this->db->db_fetch_object($result)) {
1184 array_push($matched_photos, $row['photo_id']);
1186 return $matched_photos;
1189 /* return according the selected tags */
1190 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1192 foreach($_SESSION['selected_tags'] as $tag)
1193 $selected.= $tag .",";
1194 $selected = substr($selected, 0, strlen($selected)-1);
1196 /* photo has to match at least on of the selected tags */
1197 if($_SESSION['tag_condition'] == 'or') {
1199 SELECT DISTINCT pt1.photo_id
1201 INNER JOIN photo_tags pt2
1202 ON pt1.photo_id=pt2.photo_id
1206 ON pt1.photo_id=p.id
1207 WHERE pt1.tag_id IN (". $selected .")
1209 if(!empty($additional_where_cond))
1210 $query_str.= "AND ". $additional_where_cond ." ";
1212 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1213 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1216 if(isset($order_str))
1217 $query_str.= $order_str;
1219 /* photo has to match all selected tags */
1220 elseif($_SESSION['tag_condition'] == 'and') {
1222 if(count($_SESSION['selected_tags']) >= 32) {
1223 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1224 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1228 /* Join together a table looking like
1230 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1232 so the query can quickly return all images matching the
1233 selected tags in an AND condition
1238 SELECT DISTINCT pt1.photo_id
1242 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1249 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1251 INNER JOIN photo_tags pt". ($i+2) ."
1252 ON pt1.photo_id=pt". ($i+2) .".photo_id
1257 ON pt1.photo_id=p.id
1259 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1260 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1262 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1265 if(!empty($additional_where_cond))
1266 $query_str.= "AND ". $additional_where_cond;
1268 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1269 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1272 if(isset($order_str))
1273 $query_str.= $order_str;
1277 $result = $this->db->db_query($query_str);
1278 while($row = $this->db->db_fetch_object($result)) {
1279 array_push($matched_photos, $row['photo_id']);
1281 return $matched_photos;
1284 /* return all available photos */
1286 SELECT DISTINCT p.id
1288 LEFT JOIN photo_tags pt
1294 if(!empty($additional_where_cond))
1295 $query_str.= "WHERE ". $additional_where_cond ." ";
1297 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1298 if(!empty($additional_where_cond))
1299 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1301 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1304 if(isset($order_str))
1305 $query_str.= $order_str;
1307 $result = $this->db->db_query($query_str);
1308 while($row = $this->db->db_fetch_object($result)) {
1309 array_push($matched_photos, $row['id']);
1311 return $matched_photos;
1313 } // getPhotoSelection()
1316 * control HTML ouput for photo index
1318 * this function provides all the necessary information
1319 * for the photo index template.
1322 public function showPhotoIndex()
1324 $photos = $this->getPhotoSelection();
1326 $count = count($photos);
1328 /* if all thumbnails should be shown on one page */
1329 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1333 /* thumbnails should be splitted up in several pages */
1334 elseif($this->cfg->thumbs_per_page > 0) {
1336 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1340 $begin_with = $_SESSION['begin_with'];
1343 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1347 $images[$thumbs] = Array();
1348 $img_height[$thumbs] = Array();
1349 $img_width[$thumbs] = Array();
1350 $img_id[$thumbs] = Array();
1351 $img_name[$thumbs] = Array();
1352 $img_fullname[$thumbs] = Array();
1353 $img_title = Array();
1354 $img_rating = Array();
1356 for($i = $begin_with; $i < $end_with; $i++) {
1358 if(isset($photos[$i])) {
1360 $images[$thumbs] = $photos[$i];
1361 $img_id[$thumbs] = $i;
1362 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1363 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1364 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1365 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1367 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1369 if(file_exists($thumb_path)) {
1370 $info = getimagesize($thumb_path);
1371 $img_width[$thumbs] = $info[0];
1372 $img_height[$thumbs] = $info[1];
1378 // +1 for for smarty's selection iteration
1381 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1382 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1384 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1385 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1386 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1389 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1390 $this->tmpl->assign('tag_result', 1);
1393 /* do we have to display the page selector ? */
1394 if($this->cfg->thumbs_per_page != 0) {
1398 /* calculate the page switchers */
1399 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1400 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1402 if($begin_with != 0)
1403 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1404 if($end_with < $count)
1405 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1407 $photo_per_page = $this->cfg->thumbs_per_page;
1408 $last_page = ceil($count / $photo_per_page);
1410 /* get the current selected page */
1411 if($begin_with == 0) {
1415 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1422 for($i = 1; $i <= $last_page; $i++) {
1424 if($current_page == $i)
1425 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1426 elseif($current_page-1 == $i || $current_page+1 == $i)
1427 $style = "style=\"font-size: 105%;\"";
1428 elseif(($current_page-5 >= $i) && ($i != 1) ||
1429 ($current_page+5 <= $i) && ($i != $last_page))
1430 $style = "style=\"font-size: 75%;\"";
1434 $start_with = ($i*$photo_per_page)-$photo_per_page;
1436 if($this->is_user_friendly_url()) {
1437 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1440 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1442 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1446 $select.= ">". $i ."</a> ";
1448 // until 9 pages we show the selector from 1-9
1449 if($last_page <= 9) {
1450 $page_select.= $select;
1453 if($i == 1 /* first page */ ||
1454 $i == $last_page /* last page */ ||
1455 $i == $current_page /* current page */ ||
1456 $i == ceil($last_page * 0.25) /* first quater */ ||
1457 $i == ceil($last_page * 0.5) /* half */ ||
1458 $i == ceil($last_page * 0.75) /* third quater */ ||
1459 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1460 (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 */ ||
1461 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1462 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1464 $page_select.= $select;
1472 $page_select.= "......... ";
1477 /* only show the page selector if we have more then one page */
1479 $this->tmpl->assign('page_selector', $page_select);
1482 $current_tags = $this->getCurrentTags();
1483 $extern_link = "index.php?mode=showpi";
1484 $rss_link = "index.php?mode=rss";
1485 if($current_tags != "") {
1486 $extern_link.= "&tags=". $current_tags;
1487 $rss_link.= "&tags=". $current_tags;
1489 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1490 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1491 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1494 $export_link = "index.php?mode=export";
1495 $slideshow_link = "index.php?mode=slideshow";
1497 $this->tmpl->assign('extern_link', $extern_link);
1498 $this->tmpl->assign('slideshow_link', $slideshow_link);
1499 $this->tmpl->assign('export_link', $export_link);
1500 $this->tmpl->assign('rss_link', $rss_link);
1501 $this->tmpl->assign('count', $count);
1502 $this->tmpl->assign('width', $this->cfg->thumb_width);
1503 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1504 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1505 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1506 $this->tmpl->assign('images', $images);
1507 $this->tmpl->assign('img_width', $img_width);
1508 $this->tmpl->assign('img_height', $img_height);
1509 $this->tmpl->assign('img_id', $img_id);
1510 $this->tmpl->assign('img_name', $img_name);
1511 $this->tmpl->assign('img_fullname', $img_fullname);
1512 $this->tmpl->assign('img_title', $img_title);
1513 $this->tmpl->assign('img_rating', $img_rating);
1514 $this->tmpl->assign('thumbs', $thumbs);
1515 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1517 $result = $this->tmpl->fetch("photo_index.tpl");
1519 /* if we are returning to photo index from an photo-view,
1520 scroll the window to the last shown photo-thumbnail.
1521 after this, unset the last_photo session variable.
1523 if(isset($_SESSION['last_photo'])) {
1524 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1525 unset($_SESSION['last_photo']);
1530 } // showPhotoIndex()
1533 * show credit template
1535 public function showCredits()
1537 $this->tmpl->assign('version', $this->cfg->version);
1538 $this->tmpl->assign('product', $this->cfg->product);
1539 $this->tmpl->assign('db_version', $this->dbver);
1540 $this->tmpl->show("credits.tpl");
1545 * create thumbnails for the requested width
1547 * this function creates image thumbnails of $orig_image
1548 * stored as $thumb_image. It will check if the image is
1549 * in a supported format, if necessary rotate the image
1550 * (based on EXIF orientation meta headers) and re-sizing.
1551 * @param string $orig_image
1552 * @param string $thumb_image
1553 * @param integer $width
1556 public function create_thumbnail($orig_image, $thumb_image, $width)
1558 if(!file_exists($orig_image)) {
1562 $mime = $this->get_mime_info($orig_image);
1564 /* check if original photo is a support image type */
1565 if(!$this->checkifImageSupported($mime))
1572 $meta = $this->get_meta_informations($orig_image);
1578 switch($meta['Orientation']) {
1579 case 1: /* top, left */
1580 /* nothing to do */ break;
1581 case 2: /* top, right */
1582 $rotate = 0; $flip_hori = true; break;
1583 case 3: /* bottom, left */
1584 $rotate = 180; break;
1585 case 4: /* bottom, right */
1586 $flip_vert = true; break;
1587 case 5: /* left side, top */
1588 $rotate = 90; $flip_vert = true; break;
1589 case 6: /* right side, top */
1590 $rotate = 90; break;
1591 case 7: /* left side, bottom */
1592 $rotate = 270; $flip_vert = true; break;
1593 case 8: /* right side, bottom */
1594 $rotate = 270; break;
1597 $src_img = @imagecreatefromjpeg($orig_image);
1603 $src_img = @imagecreatefrompng($orig_image);
1607 case 'image/x-portable-pixmap':
1609 $src_img = new Imagick($orig_image);
1610 $handler = "imagick";
1615 if(!isset($src_img) || empty($src_img)) {
1616 print "Can't load image from ". $orig_image ."\n";
1624 /* grabs the height and width */
1625 $cur_width = imagesx($src_img);
1626 $cur_height = imagesy($src_img);
1628 // If requested width is more then the actual image width,
1629 // do not generate a thumbnail, instead safe the original
1630 // as thumbnail but with lower quality. But if the image
1631 // is to heigh too, then we still have to resize it.
1632 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1633 $result = imagejpeg($src_img, $thumb_image, 75);
1634 imagedestroy($src_img);
1641 $cur_width = $src_img->getImageWidth();
1642 $cur_height = $src_img->getImageHeight();
1644 // If requested width is more then the actual image width,
1645 // do not generate a thumbnail, instead safe the original
1646 // as thumbnail but with lower quality. But if the image
1647 // is to heigh too, then we still have to resize it.
1648 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1649 $src_img->setCompressionQuality(75);
1650 $src_img->setImageFormat('jpeg');
1651 $src_img->writeImage($thumb_image);
1653 $src_img->destroy();
1660 // If the image will be rotate because EXIF orientation said so
1661 // 'virtually rotate' the image for further calculations
1662 if($rotate == 90 || $rotate == 270) {
1664 $cur_width = $cur_height;
1668 /* calculates aspect ratio */
1669 $aspect_ratio = $cur_height / $cur_width;
1672 if($aspect_ratio < 1) {
1674 $new_h = abs($new_w * $aspect_ratio);
1676 /* 'virtually' rotate the image and calculate it's ratio */
1677 $tmp_w = $cur_height;
1678 $tmp_h = $cur_width;
1679 /* now get the ratio from the 'rotated' image */
1680 $tmp_ratio = $tmp_h/$tmp_w;
1681 /* now calculate the new dimensions */
1683 $tmp_h = abs($tmp_w * $tmp_ratio);
1685 // now that we know, how high they photo should be, if it
1686 // gets rotated, use this high to scale the image
1688 $new_w = abs($new_h / $aspect_ratio);
1690 // If the image will be rotate because EXIF orientation said so
1691 // now 'virtually rotate' back the image for the image manipulation
1692 if($rotate == 90 || $rotate == 270) {
1703 /* creates new image of that size */
1704 $dst_img = imagecreatetruecolor($new_w, $new_h);
1706 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1708 /* copies resized portion of original image into new image */
1709 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1711 /* needs the image to be flipped horizontal? */
1713 $this->_debug("(FLIP)");
1714 $dst_img = $this->flipImage($dst_img, 'hori');
1716 /* needs the image to be flipped vertical? */
1718 $this->_debug("(FLIP)");
1719 $dst_img = $this->flipImage($dst_img, 'vert');
1723 $this->_debug("(ROTATE)");
1724 $dst_img = $this->rotateImage($dst_img, $rotate);
1727 /* write down new generated file */
1728 $result = imagejpeg($dst_img, $thumb_image, 75);
1730 /* free your mind */
1731 imagedestroy($dst_img);
1732 imagedestroy($src_img);
1734 if($result === false) {
1735 print "Can't write thumbnail ". $thumb_image ."\n";
1745 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1747 /* needs the image to be flipped horizontal? */
1749 $this->_debug("(FLIP)");
1750 $src_img->rotateImage(new ImagickPixel(), 90);
1751 $src_img->flipImage();
1752 $src_img->rotateImage(new ImagickPixel(), -90);
1754 /* needs the image to be flipped vertical? */
1756 $this->_debug("(FLIP)");
1757 $src_img->flipImage();
1761 $this->_debug("(ROTATE)");
1762 $src_img->rotateImage(new ImagickPixel(), $rotate);
1765 $src_img->setCompressionQuality(75);
1766 $src_img->setImageFormat('jpeg');
1768 if(!$src_img->writeImage($thumb_image)) {
1769 print "Can't write thumbnail ". $thumb_image ."\n";
1774 $src_img->destroy();
1781 } // create_thumbnail()
1784 * return all exif meta data from the file
1785 * @param string $file
1788 public function get_meta_informations($file)
1790 return exif_read_data($file);
1792 } // get_meta_informations()
1795 * create phpfspot own sqlite database
1797 * this function creates phpfspots own sqlite database
1798 * if it does not exist yet. this own is used to store
1799 * some necessary informations (md5 sum's, ...).
1801 public function check_config_table()
1803 // if the config table doesn't exist yet, create it
1804 if(!$this->cfg_db->db_check_table_exists("images")) {
1805 $this->cfg_db->db_exec("
1806 CREATE TABLE images (
1807 img_idx int primary key,
1813 } // check_config_table
1816 * Generates a thumbnail from photo idx
1818 * This function will generate JPEG thumbnails from provided F-Spot photo
1821 * 1. Check if all thumbnail generations (width) are already in place and
1823 * 2. Check if the md5sum of the original file has changed
1824 * 3. Generate the thumbnails if needed
1825 * @param integer $idx
1826 * @param integer $force
1827 * @param boolean $overwrite
1829 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1833 $resolutions = Array(
1834 $this->cfg->thumb_width,
1835 $this->cfg->photo_width,
1836 $this->cfg->mini_width,
1840 /* get details from F-Spot's database */
1841 $details = $this->get_photo_details($idx);
1843 /* calculate file MD5 sum */
1844 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1846 if(!file_exists($full_path)) {
1847 $this->_error("File ". $full_path ." does not exist\n");
1851 if(!is_readable($full_path)) {
1852 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1856 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1858 /* If Nikon NEF format, we need to treat it another way */
1859 if(isset($this->cfg->dcraw_bin) &&
1860 file_exists($this->cfg->dcraw_bin) &&
1861 is_executable($this->cfg->dcraw_bin) &&
1862 preg_match('/\.nef$/i', $details['uri'])) {
1864 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1866 /* if PPM file does not exist, let dcraw convert it from NEF */
1867 if(!file_exists($ppm_path)) {
1868 system($this->cfg->dcraw_bin ." -a ". $full_path);
1871 /* for now we handle the PPM instead of the NEF */
1872 $full_path = $ppm_path;
1876 $file_md5 = md5_file($full_path);
1879 foreach($resolutions as $resolution) {
1881 $generate_it = false;
1883 $thumb_sub_path = substr($file_md5, 0, 2);
1884 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1886 /* if thumbnail-subdirectory does not exist yet, create it */
1887 if(!file_exists(dirname($thumb_path))) {
1888 mkdir(dirname($thumb_path), 0755);
1891 /* if the thumbnail file doesn't exist, create it */
1892 if(!file_exists($thumb_path)) {
1893 $generate_it = true;
1895 /* if the file hasn't changed there is no need to regen the thumb */
1896 elseif($file_md5 != $this->getMD5($idx) || $force) {
1897 $generate_it = true;
1900 if($generate_it || $overwrite) {
1902 $this->_debug(" ". $resolution ."px");
1903 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1911 $this->_debug(" already exist");
1914 /* set the new/changed MD5 sum for the current photo */
1916 $this->setMD5($idx, $file_md5);
1919 $this->_debug("\n");
1924 * returns stored md5 sum for a specific photo
1926 * this function queries the phpfspot database for a
1927 * stored MD5 checksum of the specified photo
1928 * @param integer $idx
1929 * @return string|null
1931 public function getMD5($idx)
1933 $result = $this->cfg_db->db_query("
1936 WHERE img_idx='". $idx ."'
1942 $img = $this->cfg_db->db_fetch_object($result);
1943 return $img['img_md5'];
1948 * set MD5 sum for the specific photo
1949 * @param integer $idx
1950 * @param string $md5
1952 private function setMD5($idx, $md5)
1954 $result = $this->cfg_db->db_exec("
1955 REPLACE INTO images (img_idx, img_md5)
1956 VALUES ('". $idx ."', '". $md5 ."')
1962 * store current tag condition
1964 * this function stores the current tag condition
1965 * (AND or OR) in the users session variables
1966 * @param string $mode
1969 public function setTagCondition($mode)
1971 $_SESSION['tag_condition'] = $mode;
1975 } // setTagCondition()
1978 * invoke tag & date search
1980 * this function will return all matching tags and store
1981 * them in the session variable selected_tags. furthermore
1982 * it also handles the date search.
1983 * getPhotoSelection() will then only return the matching
1987 public function startSearch()
1990 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1991 $from = $_POST['from'];
1993 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1997 /* tag-name search */
1998 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1999 $searchfor_tag = $_POST['for_tag'];
2000 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2003 unset($_SESSION['searchfor_tag']);
2006 /* file-name search */
2007 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2008 $_SESSION['searchfor_name'] = $_POST['for_name'];
2011 unset($_SESSION['searchfor_name']);
2015 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2017 $_SESSION['rate_from'] = $_POST['rate_from'];
2019 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2020 $_SESSION['rate_to'] = $_POST['rate_to'];
2024 /* delete any previously set value */
2025 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2030 if(isset($from) && !empty($from))
2031 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
2033 unset($_SESSION['from_date']);
2035 if(isset($to) && !empty($to))
2036 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
2038 unset($_SESSION['to_date']);
2040 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2041 /* new search, reset the current selected tags */
2042 $_SESSION['selected_tags'] = Array();
2043 foreach($this->avail_tags as $tag) {
2044 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2045 array_push($_SESSION['selected_tags'], $tag);
2054 * updates sort order in session variable
2056 * this function is invoked by RPC and will sort the requested
2057 * sort order in the session variable.
2058 * @param string $sort_order
2061 public function updateSortOrder($order)
2063 if(isset($this->sort_orders[$order])) {
2064 $_SESSION['sort_order'] = $order;
2068 return "unkown error";
2070 } // updateSortOrder()
2075 * this function rotates the image according the
2077 * @param string $img
2078 * @param integer $degress
2081 private function rotateImage($img, $degrees)
2083 if(function_exists("imagerotate")) {
2084 $img = imagerotate($img, $degrees, 0);
2086 function imagerotate($src_img, $angle)
2088 $src_x = imagesx($src_img);
2089 $src_y = imagesy($src_img);
2090 if ($angle == 180) {
2094 elseif ($src_x <= $src_y) {
2098 elseif ($src_x >= $src_y) {
2103 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2104 imagealphablending($rotate, false);
2109 for ($y = 0; $y < ($src_y); $y++) {
2110 for ($x = 0; $x < ($src_x); $x++) {
2111 $color = imagecolorat($src_img, $x, $y);
2112 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2118 for ($y = 0; $y < ($src_y); $y++) {
2119 for ($x = 0; $x < ($src_x); $x++) {
2120 $color = imagecolorat($src_img, $x, $y);
2121 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2127 for ($y = 0; $y < ($src_y); $y++) {
2128 for ($x = 0; $x < ($src_x); $x++) {
2129 $color = imagecolorat($src_img, $x, $y);
2130 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2144 $img = imagerotate($img, $degrees);
2153 * returns flipped image
2155 * this function will return an either horizontal or
2156 * vertical flipped truecolor image.
2157 * @param string $image
2158 * @param string $mode
2161 private function flipImage($image, $mode)
2163 $w = imagesx($image);
2164 $h = imagesy($image);
2165 $flipped = imagecreatetruecolor($w, $h);
2169 for ($y = 0; $y < $h; $y++) {
2170 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2174 for ($x = 0; $x < $w; $x++) {
2175 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2185 * return all assigned tags for the specified photo
2186 * @param integer $idx
2189 private function get_photo_tags($idx)
2191 $result = $this->db->db_query("
2194 INNER JOIN photo_tags pt
2196 WHERE pt.photo_id='". $idx ."'
2201 while($row = $this->db->db_fetch_object($result)) {
2202 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2204 $tags[$row['id']] = $row['name'];
2209 } // get_photo_tags()
2212 * create on-the-fly images with text within
2213 * @param string $txt
2214 * @param string $color
2215 * @param integer $space
2216 * @param integer $font
2219 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2221 if (strlen($color) != 6)
2224 $int = hexdec($color);
2225 $h = imagefontheight($font);
2226 $fw = imagefontwidth($font);
2227 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2228 $lines = count($txt);
2229 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2230 $bg = imagecolorallocate($im, 255, 255, 255);
2231 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2234 foreach ($txt as $text) {
2235 $x = (($w - ($fw * strlen($text))) / 2);
2236 imagestring($im, $font, $x, $y, $text, $color);
2237 $y += ($h + $space);
2240 Header("Content-type: image/png");
2243 } // showTextImage()
2246 * check if all requirements are met
2249 private function check_requirements()
2251 if(!function_exists("imagecreatefromjpeg")) {
2252 print "PHP GD library extension is missing<br />\n";
2256 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2257 print "PHP SQLite3 library extension is missing<br />\n";
2261 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2262 ini_set('track_errors', 1);
2263 @include_once 'HTML/AJAX/Server.php';
2264 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2265 print "PEAR HTML_AJAX package is missing<br />\n";
2268 @include_once 'Calendar/Calendar.php';
2269 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2270 print "PEAR Calendar package is missing<br />\n";
2273 @include_once 'Console/Getopt.php';
2274 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2275 print "PEAR Console_Getopt package is missing<br />\n";
2278 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2279 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2280 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2283 ini_restore('track_errors');
2290 } // check_requirements()
2292 private function _debug($text)
2294 if($this->fromcmd) {
2301 * check if specified MIME type is supported
2302 * @param string $mime
2305 public function checkifImageSupported($mime)
2307 $supported_types = Array(
2310 "image/x-portable-pixmap",
2314 if(in_array($mime, $supported_types))
2319 } // checkifImageSupported()
2323 * @param string $text
2325 public function _error($text)
2327 switch($this->cfg->logging) {
2330 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2331 print $text ."<br />\n";
2337 error_log($text, 3, $his->cfg->log_file);
2341 $this->runtime_error = true;
2346 * output calendard input fields
2347 * @param string $mode
2350 private function get_calendar($mode)
2352 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2353 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2354 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2356 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2357 if(!isset($_SESSION[$mode .'_date']))
2358 $output.= " disabled=\"disabled\"";
2360 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2361 if(!isset($_SESSION[$mode .'_date']))
2362 $output.= " disabled=\"disabled\"";
2364 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2365 if(!isset($_SESSION[$mode .'_date']))
2366 $output.= " disabled=\"disabled\"";
2374 * output calendar matrix
2375 * @param integer $year
2376 * @param integer $month
2377 * @param integer $day
2379 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2381 if (!isset($year)) $year = date('Y');
2382 if (!isset($month)) $month = date('m');
2383 if (!isset($day)) $day = date('d');
2388 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2389 require_once CALENDAR_ROOT.'Day.php';
2392 $month = new Calendar_Month_Weekdays($year,$month);
2395 $prevStamp = $month->prevMonth(true);
2396 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2397 $nextStamp = $month->nextMonth(true);
2398 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2400 $selectedDays = array (
2401 new Calendar_Day($year,$month,$day),
2402 new Calendar_Day($year,12,25),
2405 // Build the days in the month
2406 $month->build($selectedDays);
2408 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2409 $this->tmpl->assign('prev_month', $prev);
2410 $this->tmpl->assign('next_month', $next);
2412 while ( $day = $month->fetch() ) {
2414 if(!isset($matrix[$rows]))
2415 $matrix[$rows] = Array();
2419 $dayStamp = $day->thisDay(true);
2420 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2422 // isFirst() to find start of week
2423 if ( $day->isFirst() )
2426 if ( $day->isSelected() ) {
2427 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2428 } else if ( $day->isEmpty() ) {
2429 $string.= "<td> </td>\n";
2431 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2434 // isLast() to find end of week
2435 if ( $day->isLast() )
2436 $string.= "</tr>\n";
2438 $matrix[$rows][$cols] = $string;
2448 $this->tmpl->assign('matrix', $matrix);
2449 $this->tmpl->assign('rows', $rows);
2450 $this->tmpl->show("calendar.tpl");
2452 } // get_calendar_matrix()
2455 * output export page
2456 * @param string $mode
2458 public function getExport($mode)
2460 $pictures = $this->getPhotoSelection();
2461 $current_tags = $this->getCurrentTags();
2463 foreach($pictures as $picture) {
2465 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2466 if($current_tags != "") {
2467 $orig_url.= "&tags=". $current_tags;
2469 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2470 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2473 if($this->is_user_friendly_url()) {
2474 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2477 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2483 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2484 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2488 // "[%pictureurl% %thumbnailurl%]"
2489 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2492 case 'MoinMoinList':
2493 // " * [%pictureurl% %thumbnailurl%]"
2494 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2505 public function getRSSFeed()
2507 Header("Content-type: text/xml; charset=utf-8");
2508 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2511 xmlns:media="http://search.yahoo.com/mrss/"
2512 xmlns:dc="http://purl.org/dc/elements/1.1/"
2515 <title>phpfspot</title>
2516 <description>phpfspot RSS feed</description>
2517 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2518 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2519 <generator>phpfspot</generator>
2522 $pictures = $this->getPhotoSelection();
2523 $current_tags = $this->getCurrentTags();
2525 foreach($pictures as $picture) {
2527 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2528 if($current_tags != "") {
2529 $orig_url.= "&tags=". $current_tags;
2531 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2532 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2535 $details = $this->get_photo_details($picture);
2537 if($this->is_user_friendly_url()) {
2538 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2541 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2544 $thumb_html = htmlspecialchars("
2545 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2547 ". $details['description']);
2549 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2551 /* get EXIF information if JPEG */
2552 if($details['mime'] == "image/jpeg") {
2553 $meta = $this->get_meta_informations($orig_path);
2556 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2560 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2561 <link><?php print htmlspecialchars($orig_url); ?></link>
2562 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2563 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2565 <?php print $thumb_html; ?>
2567 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2582 * return all selected tags as one string
2585 private function getCurrentTags()
2588 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2589 foreach($_SESSION['selected_tags'] as $tag)
2590 $current_tags.= $tag .",";
2591 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2593 return $current_tags;
2595 } // getCurrentTags()
2598 * return the current photo
2600 public function getCurrentPhoto()
2602 if(isset($_SESSION['current_photo'])) {
2603 print $_SESSION['current_photo'];
2605 } // getCurrentPhoto()
2608 * tells the client browser what to do
2610 * this function is getting called via AJAX by the
2611 * client browsers. it will tell them what they have
2612 * to do next. This is necessary for directly jumping
2613 * into photo index or single photo view when the are
2614 * requested with specific URLs
2617 public function whatToDo()
2619 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2621 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2622 return "showpi_tags";
2624 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2631 * return the current process-user
2634 private function getuid()
2636 if($uid = posix_getuid()) {
2637 if($user = posix_getpwuid($uid)) {
2638 return $user['name'];
2647 * returns a select-dropdown box to select photo index sort parameters
2648 * @param array $params
2649 * @param smarty $smarty
2652 public function smarty_sort_select_list($params, &$smarty)
2656 foreach($this->sort_orders as $key => $value) {
2657 $output.= "<option value=\"". $key ."\"";
2658 if($key == $_SESSION['sort_order']) {
2659 $output.= " selected=\"selected\"";
2661 $output.= ">". $value ."</option>";
2666 } // smarty_sort_select_list()
2669 * returns the currently selected sort order
2672 private function get_sort_order()
2674 switch($_SESSION['sort_order']) {
2676 return " ORDER BY p.time ASC";
2679 return " ORDER BY p.time DESC";
2682 if($this->dbver < 9) {
2683 return " ORDER BY p.name ASC";
2686 return " ORDER BY basename(p.uri) ASC";
2690 if($this->dbver < 9) {
2691 return " ORDER BY p.name DESC";
2694 return " ORDER BY basename(p.uri) DESC";
2698 return " ORDER BY t.name ASC ,p.time ASC";
2701 return " ORDER BY t.name DESC ,p.time ASC";
2704 return " ORDER BY t.name ASC, p.rating ASC";
2707 return " ORDER BY t.name DESC, p.rating DESC";
2711 } // get_sort_order()
2714 * return the next to be shown slide show image
2716 * this function returns the URL of the next image
2717 * in the slideshow sequence.
2720 public function getNextSlideShowImage()
2722 $all_photos = $this->getPhotoSelection();
2724 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2725 $_SESSION['slideshow_img'] = 0;
2727 $_SESSION['slideshow_img']++;
2729 if($this->is_user_friendly_url()) {
2730 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2733 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2735 } // getNextSlideShowImage()
2738 * return the previous to be shown slide show image
2740 * this function returns the URL of the previous image
2741 * in the slideshow sequence.
2744 public function getPrevSlideShowImage()
2746 $all_photos = $this->getPhotoSelection();
2748 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2749 $_SESSION['slideshow_img'] = 0;
2751 $_SESSION['slideshow_img']--;
2753 if($this->is_user_friendly_url()) {
2754 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2757 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2759 } // getPrevSlideShowImage()
2761 public function resetSlideShow()
2763 if(isset($_SESSION['slideshow_img']))
2764 unset($_SESSION['slideshow_img']);
2766 } // resetSlideShow()
2771 * this function will get all photos from the fspot
2772 * database and randomly return ONE entry
2774 * saddly there is yet no sqlite3 function which returns
2775 * the bulk result in array, so we have to fill up our
2779 public function get_random_photo()
2788 /* if show_tags is set, only return details for photos which
2789 are specified to be shown
2791 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2793 INNER JOIN photo_tags pt
2798 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2801 $result = $this->db->db_query($query_str);
2803 while($row = $this->db->db_fetch_object($result)) {
2804 array_push($all, $row['id']);
2807 return $all[array_rand($all)];
2809 } // get_random_photo()
2812 * get random photo tag photo
2814 * this function will get all photos tagged with the requested
2815 * tag from the fspot database and randomly return ONE entry
2817 * saddly there is yet no sqlite3 function which returns
2818 * the bulk result in array, so we have to fill up our
2822 public function get_random_tag_photo($tagidx)
2829 INNER JOIN photo_tags pt
2833 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2841 pt.tag_id LIKE '". $tagidx ."'
2844 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2847 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2851 $result = $this->db->db_query($query_str);
2853 while($row = $this->db->db_fetch_object($result)) {
2854 array_push($all, $row['id']);
2857 return $all[array_rand($all)];
2859 } // get_random_tag_photo()
2862 * validates provided date
2864 * this function validates if the provided date
2865 * contains a valid date and will return true
2867 * @param string $date_str
2870 public function isValidDate($date_str)
2872 $timestamp = strtotime($date_str);
2874 if(is_numeric($timestamp))
2882 * timestamp to string conversion
2883 * @param integer $timestamp
2886 private function ts2str($timestamp)
2888 if(!empty($timestamp) && is_numeric($timestamp))
2889 return strftime("%Y-%m-%d", $timestamp);
2894 * extract tag-names from $_GET['tags']
2895 * @param string $tags_str
2898 private function extractTags($tags_str)
2900 $not_validated = split(',', $tags_str);
2901 $validated = array();
2903 foreach($not_validated as $tag) {
2904 if(is_numeric($tag))
2905 array_push($validated, $tag);
2913 * returns the full path to a thumbnail
2914 * @param integer $width
2915 * @param integer $photo
2918 public function get_thumb_path($width, $photo)
2920 $md5 = $this->getMD5($photo);
2921 $sub_path = substr($md5, 0, 2);
2922 return $this->cfg->thumb_path
2930 } // get_thumb_path()
2933 * returns server's virtual host name
2936 private function get_server_name()
2938 return $_SERVER['SERVER_NAME'];
2939 } // get_server_name()
2942 * returns type of webprotocol which is currently used
2945 private function get_web_protocol()
2947 if(!isset($_SERVER['HTTPS']))
2951 } // get_web_protocol()
2954 * return url to this phpfspot installation
2957 private function get_phpfspot_url()
2959 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2961 } // get_phpfspot_url()
2964 * returns the number of photos which are tagged with $tag_id
2965 * @param integer $tag_id
2968 public function get_num_photos($tag_id)
2970 if($result = $this->db->db_fetchSingleRow("
2971 SELECT count(*) as number
2974 tag_id LIKE '". $tag_id ."'")) {
2976 return $result['number'];
2982 } // get_num_photos()
2985 * check file exists and is readable
2987 * returns true, if everything is ok, otherwise false
2988 * if $silent is not set, this function will output and
2990 * @param string $file
2991 * @param boolean $silent
2994 private function check_readable($file, $silent = null)
2996 if(!file_exists($file)) {
2998 print "File \"". $file ."\" does not exist.\n";
3002 if(!is_readable($file)) {
3004 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3010 } // check_readable()
3013 * check if all needed indices are present
3015 * this function checks, if some needed indices are already
3016 * present, or if not, create them on the fly. they are
3017 * necessary to speed up some queries like that one look for
3018 * all tags, when show_tags is specified in the configuration.
3020 private function checkDbIndices()
3022 $result = $this->db->db_exec("
3023 CREATE INDEX IF NOT EXISTS
3030 } // checkDbIndices()
3033 * retrive F-Spot database version
3035 * this function will return the F-Spot database version number
3036 * It is stored within the sqlite3 database in the table meta
3037 * @return string|null
3039 public function getFspotDBVersion()
3041 if($result = $this->db->db_fetchSingleRow("
3042 SELECT data as version
3045 name LIKE 'F-Spot Database Version'
3047 return $result['version'];
3051 } // getFspotDBVersion()
3054 * parse the provided URI and will returned the requested chunk
3055 * @param string $uri
3056 * @param string $mode
3059 public function parse_uri($uri, $mode)
3061 if(($components = parse_url($uri)) !== false) {
3065 return basename($components['path']);
3068 return dirname($components['path']);
3071 return $components['path'];
3081 * validate config options
3083 * this function checks if all necessary configuration options are
3084 * specified and set.
3087 private function check_config_options()
3089 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3090 $this->_error("Please set \$page_title in phpfspot_cfg");
3092 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3093 $this->_error("Please set \$base_path in phpfspot_cfg");
3095 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3096 $this->_error("Please set \$web_path in phpfspot_cfg");
3098 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3099 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3101 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3102 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3104 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3105 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3107 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3108 $this->_error("Please set \$db_access in phpfspot_cfg");
3110 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3111 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3113 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3114 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3116 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3117 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3119 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3120 $this->_error("Please set \$photo_width in phpfspot_cfg");
3122 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3123 $this->_error("Please set \$mini_width in phpfspot_cfg");
3125 if(!isset($this->cfg->thumbs_per_page))
3126 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3128 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3129 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3131 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3132 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3134 if(!isset($this->cfg->hide_tags))
3135 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3137 if(!isset($this->cfg->theme_name))
3138 $this->_error("Please set \$theme_name in phpfspot_cfg");
3140 if(!isset($this->cfg->logging))
3141 $this->_error("Please set \$logging in phpfspot_cfg");
3143 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3145 if(!isset($this->cfg->log_file))
3146 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3148 if(!is_writeable($this->cfg->log_file))
3149 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3153 /* remove trailing slash, if set */
3154 if($this->cfg->web_path == "/")
3155 $this->cfg->web_path = "";
3156 elseif(preg_match('/\/$/', $this->cfg->web_path))
3157 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3159 return $this->runtime_error;
3161 } // check_config_options()
3164 * cleanup phpfspot own database
3166 * When photos are getting delete from F-Spot, there will remain
3167 * remain some residues in phpfspot own database. This function
3168 * will try to wipe them out.
3170 public function cleanup_phpfspot_db()
3172 $to_delete = Array();
3174 $result = $this->cfg_db->db_query("
3177 ORDER BY img_idx ASC
3180 while($row = $this->cfg_db->db_fetch_object($result)) {
3181 if(!$this->db->db_fetchSingleRow("
3184 WHERE id='". $row['img_idx'] ."'")) {
3186 array_push($to_delete, $row['img_idx'], ',');
3190 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3192 $this->cfg_db->db_exec("
3194 WHERE img_idx IN (". implode($to_delete) .")
3197 } // cleanup_phpfspot_db()
3200 * return first image of the page, the $current photo
3203 * this function is used to find out the first photo of the
3204 * current page, in which the $current photo lies. this is
3205 * used to display the correct photo, when calling showPhotoIndex()
3207 * @param integer $current
3208 * @param integer $max
3211 private function getCurrentPage($current, $max)
3213 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3214 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3215 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3221 } // getCurrentPage()
3226 * this function tries to find out the correct mime-type
3227 * for the provided file.
3228 * @param string $file
3231 public function get_mime_info($file)
3233 $details = getimagesize($file);
3235 /* if getimagesize() returns empty, try at least to find out the
3238 if(empty($details) && function_exists('mime_content_type')) {
3240 // mime_content_type is marked as deprecated in the documentation,
3241 // but is it really necessary to force users to install a PECL
3243 $details['mime'] = mime_content_type($file);
3246 return $details['mime'];
3248 } // get_mime_info()
3251 * return tag-name by tag-idx
3253 * this function returns the tag-name for the requested
3254 * tag specified by tag-idx.
3255 * @param integer $idx
3258 public function get_tag_name($idx)
3260 if($result = $this->db->db_fetchSingleRow("
3264 id LIKE '". $idx ."'")) {
3266 return $result['name'];
3275 * parse user friendly url which got rewritten by the websever
3276 * @param string $request_uri
3279 private function parse_user_friendly_url($request_uri)
3281 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3283 $options = explode('/', $request_uri);
3285 switch($options[1]) {
3287 if(is_numeric($options[2])) {
3288 $this->session_cleanup();
3289 //unset($_SESSION['start_action']);
3290 //unset($_SESSION['selected_tags']);
3291 $_GET['mode'] = 'showp';
3292 return $this->showPhoto($options[2]);
3296 if(is_numeric($options[2])) {
3297 require_once "phpfspot_img.php";
3298 $img = new PHPFSPOT_IMG;
3299 if(isset($options[3]) && is_numeric($options[3]))
3300 $img->showImg($options[2], $options[3]);
3302 $img->showImg($options[2]);
3307 if(is_numeric($options[2])) {
3308 $this->session_cleanup();
3309 $_GET['tags'] = $options[2];
3310 $_SESSION['selected_tags'] = Array($options[2]);
3311 if(isset($options[3]) && is_numeric($options[3]))
3312 $_SESSION['begin_with'] = $options[3];
3313 return $this->showPhotoIndex();
3319 } // parse_user_friendly_url()
3322 * check if user-friendly-urls are enabled
3324 * this function will return true, if the config option
3325 * $user_friendly_url has been set. Otherwise false.
3328 private function is_user_friendly_url()
3330 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3335 } // is_user_friendly_url()
3340 * this function will cleanup user's session information
3342 private function session_cleanup()
3344 unset($_SESSION['begin_with']);
3345 $this->resetDateSearch();
3346 $this->resetPhotoView();
3347 $this->resetTagSearch();
3348 $this->resetNameSearch();
3349 $this->resetDateSearch();
3352 } // session_cleanup()