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(isset($info['mime']) && $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);
751 if(isset($previous_img)) {
752 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
753 $this->tmpl->assign('prev_img', $previous_img);
756 if(isset($next_img)) {
757 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
758 $this->tmpl->assign('next_img', $next_img);
761 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
762 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
763 $this->tmpl->assign('photo_number', $i);
764 $this->tmpl->assign('photo_count', count($all_photos));
766 return $this->tmpl->fetch("single_photo.tpl");
771 * all available tags and tag cloud
773 * this function outputs all available tags (time ordered)
774 * and in addition output them as tag cloud (tags which have
775 * many photos will appears more then others)
777 public function getAvailableTags()
779 /* retrive tags from database */
784 $result = $this->db->db_query("
785 SELECT tag_id as id, count(tag_id) as quantity
795 while($row = $this->db->db_fetch_object($result)) {
796 $tags[$row['id']] = $row['quantity'];
799 // change these font sizes if you will
800 $max_size = 125; // max font size in %
801 $min_size = 75; // min font size in %
804 $max_sat = hexdec('cc');
805 $min_sat = hexdec('44');
807 // get the largest and smallest array values
808 $max_qty = max(array_values($tags));
809 $min_qty = min(array_values($tags));
811 // find the range of values
812 $spread = $max_qty - $min_qty;
813 if (0 == $spread) { // we don't want to divide by zero
817 // determine the font-size increment
818 // this is the increase per tag quantity (times used)
819 $step = ($max_size - $min_size)/($spread);
820 $step_sat = ($max_sat - $min_sat)/($spread);
822 // loop through our tag array
823 foreach ($tags as $key => $value) {
825 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
828 // calculate CSS font-size
829 // find the $value in excess of $min_qty
830 // multiply by the font-size increment ($size)
831 // and add the $min_size set above
832 $size = $min_size + (($value - $min_qty) * $step);
833 // uncomment if you want sizes in whole %:
836 $color = $min_sat + ($value - $min_qty) * $step_sat;
842 if(isset($this->tags[$key])) {
843 if($this->is_user_friendly_url())
844 $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>, ";
846 $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>, ";
850 $output = substr($output, 0, strlen($output)-2);
853 } // getAvailableTags()
856 * output all selected tags
858 * this function output all tags which have been selected
859 * by the user. the selected tags are stored in the
860 * session-variable $_SESSION['selected_tags']
863 public function getSelectedTags($type = 'link')
865 /* retrive tags from database */
870 foreach($this->avail_tags as $tag)
872 // return all selected tags
873 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
878 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
882 <div class=\"tagresulttag\">
883 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
884 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
894 $output = substr($output, 0, strlen($output)-2);
898 return "no tags selected";
901 } // getSelectedTags()
904 * add tag to users session variable
906 * this function will add the specified to users current
907 * tag selection. if a date search has been made before
908 * it will be now cleared
911 public function addTag($tag)
913 if(!isset($_SESSION['selected_tags']))
914 $_SESSION['selected_tags'] = Array();
916 if(isset($_SESSION['searchfor_tag']))
917 unset($_SESSION['searchfor_tag']);
919 // has the user requested to hide this tag, and still someone,
920 // somehow tries to add it, don't allow this.
921 if(!isset($this->cfg->hide_tags) &&
922 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
925 if(!in_array($tag, $_SESSION['selected_tags']))
926 array_push($_SESSION['selected_tags'], $tag);
933 * remove tag to users session variable
935 * this function removes the specified tag from
936 * users current tag selection
940 public function delTag($tag)
942 if(isset($_SESSION['searchfor_tag']))
943 unset($_SESSION['searchfor_tag']);
945 if(isset($_SESSION['selected_tags'])) {
946 $key = array_search($tag, $_SESSION['selected_tags']);
947 unset($_SESSION['selected_tags'][$key]);
948 sort($_SESSION['selected_tags']);
956 * reset tag selection
958 * if there is any tag selection, it will be
961 public function resetTags()
963 if(isset($_SESSION['selected_tags']))
964 unset($_SESSION['selected_tags']);
969 * returns the value for the autocomplete tag-search
972 public function get_xml_tag_list()
974 if(!isset($_GET['search']) || !is_string($_GET['search']))
975 $_GET['search'] = '';
980 /* retrive tags from database */
983 $matched_tags = Array();
985 header("Content-Type: text/xml");
987 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
988 $string.= "<results>\n";
990 foreach($this->avail_tags as $tag)
992 if(!empty($_GET['search']) &&
993 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
994 count($matched_tags) < $length) {
996 $count = $this->get_num_photos($tag);
999 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1002 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1008 /* if we have collected enough items, break out */
1009 if(count($matched_tags) >= $length)
1013 $string.= "</results>\n";
1017 } // get_xml_tag_list()
1021 * reset single photo
1023 * if a specific photo was requested (external link)
1024 * unset the session variable now
1026 public function resetPhotoView()
1028 if(isset($_SESSION['current_photo']))
1029 unset($_SESSION['current_photo']);
1031 } // resetPhotoView();
1036 * if any tag search has taken place, reset it now
1038 public function resetTagSearch()
1040 if(isset($_SESSION['searchfor_tag']))
1041 unset($_SESSION['searchfor_tag']);
1043 } // resetTagSearch()
1048 * if any name search has taken place, reset it now
1050 public function resetNameSearch()
1052 if(isset($_SESSION['searchfor_name']))
1053 unset($_SESSION['searchfor_name']);
1055 } // resetNameSearch()
1060 * if any date search has taken place, reset it now.
1062 public function resetDateSearch()
1064 if(isset($_SESSION['from_date']))
1065 unset($_SESSION['from_date']);
1066 if(isset($_SESSION['to_date']))
1067 unset($_SESSION['to_date']);
1069 } // resetDateSearch();
1074 * if any rate search has taken place, reset it now.
1076 public function resetRateSearch()
1078 if(isset($_SESSION['rate_from']))
1079 unset($_SESSION['rate_from']);
1080 if(isset($_SESSION['rate_to']))
1081 unset($_SESSION['rate_to']);
1083 } // resetRateSearch();
1086 * return all photo according selection
1088 * this function returns all photos based on
1089 * the tag-selection, tag- or date-search.
1090 * the tag-search also has to take care of AND
1091 * and OR conjunctions
1094 public function getPhotoSelection()
1096 $matched_photos = Array();
1097 $additional_where_cond = "";
1099 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1100 $from_date = $_SESSION['from_date'];
1101 $to_date = $_SESSION['to_date'];
1102 $additional_where_cond.= "
1103 p.time>='". $from_date ."'
1105 p.time<='". $to_date ."'
1109 if(isset($_SESSION['searchfor_name'])) {
1111 /* check for previous conditions. if so add 'AND' */
1112 if(!empty($additional_where_cond)) {
1113 $additional_where_cond.= " AND ";
1116 if($this->dbver < 9) {
1117 $additional_where_cond.= "
1119 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1121 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1126 $additional_where_cond.= "
1128 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1130 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1136 /* limit result based on rate-search */
1137 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1139 if($this->dbver > 10) {
1141 /* check for previous conditions. if so add 'AND' */
1142 if(!empty($additional_where_cond)) {
1143 $additional_where_cond.= " AND ";
1146 $additional_where_cond.= "
1147 p.rating >= ". $_SESSION['rate_from'] ."
1149 p.rating <= ". $_SESSION['rate_to'] ."
1154 if(isset($_SESSION['sort_order'])) {
1155 $order_str = $this->get_sort_order();
1158 /* return a search result */
1159 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1161 SELECT DISTINCT pt1.photo_id
1163 INNER JOIN photo_tags pt2
1164 ON pt1.photo_id=pt2.photo_id
1168 ON pt1.photo_id=p.id
1171 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1173 if(!empty($additional_where_cond))
1174 $query_str.= "AND ". $additional_where_cond ." ";
1176 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1177 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1180 if(isset($order_str))
1181 $query_str.= $order_str;
1183 $result = $this->db->db_query($query_str);
1184 while($row = $this->db->db_fetch_object($result)) {
1185 array_push($matched_photos, $row['photo_id']);
1187 return $matched_photos;
1190 /* return according the selected tags */
1191 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1193 foreach($_SESSION['selected_tags'] as $tag)
1194 $selected.= $tag .",";
1195 $selected = substr($selected, 0, strlen($selected)-1);
1197 /* photo has to match at least on of the selected tags */
1198 if($_SESSION['tag_condition'] == 'or') {
1200 SELECT DISTINCT pt1.photo_id
1202 INNER JOIN photo_tags pt2
1203 ON pt1.photo_id=pt2.photo_id
1207 ON pt1.photo_id=p.id
1208 WHERE pt1.tag_id IN (". $selected .")
1210 if(!empty($additional_where_cond))
1211 $query_str.= "AND ". $additional_where_cond ." ";
1213 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1214 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1217 if(isset($order_str))
1218 $query_str.= $order_str;
1220 /* photo has to match all selected tags */
1221 elseif($_SESSION['tag_condition'] == 'and') {
1223 if(count($_SESSION['selected_tags']) >= 32) {
1224 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1225 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1229 /* Join together a table looking like
1231 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1233 so the query can quickly return all images matching the
1234 selected tags in an AND condition
1239 SELECT DISTINCT pt1.photo_id
1243 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1250 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1252 INNER JOIN photo_tags pt". ($i+2) ."
1253 ON pt1.photo_id=pt". ($i+2) .".photo_id
1258 ON pt1.photo_id=p.id
1260 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1261 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1263 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1266 if(!empty($additional_where_cond))
1267 $query_str.= "AND ". $additional_where_cond;
1269 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1270 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1273 if(isset($order_str))
1274 $query_str.= $order_str;
1278 $result = $this->db->db_query($query_str);
1279 while($row = $this->db->db_fetch_object($result)) {
1280 array_push($matched_photos, $row['photo_id']);
1282 return $matched_photos;
1285 /* return all available photos */
1287 SELECT DISTINCT p.id
1289 LEFT JOIN photo_tags pt
1295 if(!empty($additional_where_cond))
1296 $query_str.= "WHERE ". $additional_where_cond ." ";
1298 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1299 if(!empty($additional_where_cond))
1300 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1302 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1305 if(isset($order_str))
1306 $query_str.= $order_str;
1308 $result = $this->db->db_query($query_str);
1309 while($row = $this->db->db_fetch_object($result)) {
1310 array_push($matched_photos, $row['id']);
1312 return $matched_photos;
1314 } // getPhotoSelection()
1317 * control HTML ouput for photo index
1319 * this function provides all the necessary information
1320 * for the photo index template.
1323 public function showPhotoIndex()
1325 $photos = $this->getPhotoSelection();
1326 $current_tags = $this->getCurrentTags();
1328 $count = count($photos);
1330 /* if all thumbnails should be shown on one page */
1331 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1335 /* thumbnails should be splitted up in several pages */
1336 elseif($this->cfg->thumbs_per_page > 0) {
1338 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1342 $begin_with = $_SESSION['begin_with'];
1345 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1349 $images[$thumbs] = Array();
1350 $img_height[$thumbs] = Array();
1351 $img_width[$thumbs] = Array();
1352 $img_id[$thumbs] = Array();
1353 $img_name[$thumbs] = Array();
1354 $img_fullname[$thumbs] = Array();
1355 $img_title = Array();
1356 $img_rating = Array();
1358 for($i = $begin_with; $i < $end_with; $i++) {
1360 if(isset($photos[$i])) {
1362 $images[$thumbs] = $photos[$i];
1363 $img_id[$thumbs] = $i;
1364 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1365 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1366 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1367 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1369 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1371 if(file_exists($thumb_path)) {
1372 $info = getimagesize($thumb_path);
1373 $img_width[$thumbs] = $info[0];
1374 $img_height[$thumbs] = $info[1];
1380 // +1 for for smarty's selection iteration
1383 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1384 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1386 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1387 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1388 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1391 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1392 $this->tmpl->assign('tag_result', 1);
1395 /* do we have to display the page selector ? */
1396 if($this->cfg->thumbs_per_page != 0) {
1400 /* calculate the page switchers */
1401 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1402 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1404 if($begin_with != 0)
1405 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1406 if($end_with < $count)
1407 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1409 $photo_per_page = $this->cfg->thumbs_per_page;
1410 $last_page = ceil($count / $photo_per_page);
1412 /* get the current selected page */
1413 if($begin_with == 0) {
1417 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1424 for($i = 1; $i <= $last_page; $i++) {
1426 if($current_page == $i)
1427 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1428 elseif($current_page-1 == $i || $current_page+1 == $i)
1429 $style = "style=\"font-size: 105%;\"";
1430 elseif(($current_page-5 >= $i) && ($i != 1) ||
1431 ($current_page+5 <= $i) && ($i != $last_page))
1432 $style = "style=\"font-size: 75%;\"";
1436 $start_with = ($i*$photo_per_page)-$photo_per_page;
1438 if($this->is_user_friendly_url()) {
1439 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1442 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1444 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1448 $select.= ">". $i ."</a> ";
1450 // until 9 pages we show the selector from 1-9
1451 if($last_page <= 9) {
1452 $page_select.= $select;
1455 if($i == 1 /* first page */ ||
1456 $i == $last_page /* last page */ ||
1457 $i == $current_page /* current page */ ||
1458 $i == ceil($last_page * 0.25) /* first quater */ ||
1459 $i == ceil($last_page * 0.5) /* half */ ||
1460 $i == ceil($last_page * 0.75) /* third quater */ ||
1461 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1462 (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 */ ||
1463 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1464 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1466 $page_select.= $select;
1474 $page_select.= "......... ";
1479 /* only show the page selector if we have more then one page */
1481 $this->tmpl->assign('page_selector', $page_select);
1484 $extern_link = "index.php?mode=showpi";
1485 $rss_link = "index.php?mode=rss";
1486 if($current_tags != "") {
1487 $extern_link.= "&tags=". $current_tags;
1488 $rss_link.= "&tags=". $current_tags;
1490 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1491 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1492 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1495 $export_link = "index.php?mode=export";
1496 $slideshow_link = "index.php?mode=slideshow";
1498 $this->tmpl->assign('extern_link', $extern_link);
1499 $this->tmpl->assign('slideshow_link', $slideshow_link);
1500 $this->tmpl->assign('export_link', $export_link);
1501 $this->tmpl->assign('rss_link', $rss_link);
1502 $this->tmpl->assign('count', $count);
1503 $this->tmpl->assign('width', $this->cfg->thumb_width);
1504 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1505 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1506 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1507 $this->tmpl->assign('images', $images);
1508 $this->tmpl->assign('img_width', $img_width);
1509 $this->tmpl->assign('img_height', $img_height);
1510 $this->tmpl->assign('img_id', $img_id);
1511 $this->tmpl->assign('img_name', $img_name);
1512 $this->tmpl->assign('img_fullname', $img_fullname);
1513 $this->tmpl->assign('img_title', $img_title);
1514 $this->tmpl->assign('img_rating', $img_rating);
1515 $this->tmpl->assign('thumbs', $thumbs);
1516 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1518 $result = $this->tmpl->fetch("photo_index.tpl");
1520 /* if we are returning to photo index from an photo-view,
1521 scroll the window to the last shown photo-thumbnail.
1522 after this, unset the last_photo session variable.
1524 if(isset($_SESSION['last_photo'])) {
1525 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1526 unset($_SESSION['last_photo']);
1531 } // showPhotoIndex()
1534 * show credit template
1536 public function showCredits()
1538 $this->tmpl->assign('version', $this->cfg->version);
1539 $this->tmpl->assign('product', $this->cfg->product);
1540 $this->tmpl->assign('db_version', $this->dbver);
1541 $this->tmpl->show("credits.tpl");
1546 * create thumbnails for the requested width
1548 * this function creates image thumbnails of $orig_image
1549 * stored as $thumb_image. It will check if the image is
1550 * in a supported format, if necessary rotate the image
1551 * (based on EXIF orientation meta headers) and re-sizing.
1552 * @param string $orig_image
1553 * @param string $thumb_image
1554 * @param integer $width
1557 public function create_thumbnail($orig_image, $thumb_image, $width)
1559 if(!file_exists($orig_image)) {
1563 $mime = $this->get_mime_info($orig_image);
1565 /* check if original photo is a support image type */
1566 if(!$this->checkifImageSupported($mime))
1573 $meta = $this->get_meta_informations($orig_image);
1579 if(isset($meta['Orientation'])) {
1580 switch($meta['Orientation']) {
1581 case 1: /* top, left */
1582 /* nothing to do */ break;
1583 case 2: /* top, right */
1584 $rotate = 0; $flip_hori = true; break;
1585 case 3: /* bottom, left */
1586 $rotate = 180; break;
1587 case 4: /* bottom, right */
1588 $flip_vert = true; break;
1589 case 5: /* left side, top */
1590 $rotate = 90; $flip_vert = true; break;
1591 case 6: /* right side, top */
1592 $rotate = 90; break;
1593 case 7: /* left side, bottom */
1594 $rotate = 270; $flip_vert = true; break;
1595 case 8: /* right side, bottom */
1596 $rotate = 270; break;
1600 $src_img = @imagecreatefromjpeg($orig_image);
1606 $src_img = @imagecreatefrompng($orig_image);
1610 case 'image/x-portable-pixmap':
1612 $src_img = new Imagick($orig_image);
1613 $handler = "imagick";
1618 if(!isset($src_img) || empty($src_img)) {
1619 print "Can't load image from ". $orig_image ."\n";
1627 /* grabs the height and width */
1628 $cur_width = imagesx($src_img);
1629 $cur_height = imagesy($src_img);
1631 // If requested width is more then the actual image width,
1632 // do not generate a thumbnail, instead safe the original
1633 // as thumbnail but with lower quality. But if the image
1634 // is to heigh too, then we still have to resize it.
1635 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1636 $result = imagejpeg($src_img, $thumb_image, 75);
1637 imagedestroy($src_img);
1644 $cur_width = $src_img->getImageWidth();
1645 $cur_height = $src_img->getImageHeight();
1647 // If requested width is more then the actual image width,
1648 // do not generate a thumbnail, instead safe the original
1649 // as thumbnail but with lower quality. But if the image
1650 // is to heigh too, then we still have to resize it.
1651 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1652 $src_img->setCompressionQuality(75);
1653 $src_img->setImageFormat('jpeg');
1654 $src_img->writeImage($thumb_image);
1656 $src_img->destroy();
1663 // If the image will be rotate because EXIF orientation said so
1664 // 'virtually rotate' the image for further calculations
1665 if($rotate == 90 || $rotate == 270) {
1667 $cur_width = $cur_height;
1671 /* calculates aspect ratio */
1672 $aspect_ratio = $cur_height / $cur_width;
1675 if($aspect_ratio < 1) {
1677 $new_h = abs($new_w * $aspect_ratio);
1679 /* 'virtually' rotate the image and calculate it's ratio */
1680 $tmp_w = $cur_height;
1681 $tmp_h = $cur_width;
1682 /* now get the ratio from the 'rotated' image */
1683 $tmp_ratio = $tmp_h/$tmp_w;
1684 /* now calculate the new dimensions */
1686 $tmp_h = abs($tmp_w * $tmp_ratio);
1688 // now that we know, how high they photo should be, if it
1689 // gets rotated, use this high to scale the image
1691 $new_w = abs($new_h / $aspect_ratio);
1693 // If the image will be rotate because EXIF orientation said so
1694 // now 'virtually rotate' back the image for the image manipulation
1695 if($rotate == 90 || $rotate == 270) {
1706 /* creates new image of that size */
1707 $dst_img = imagecreatetruecolor($new_w, $new_h);
1709 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1711 /* copies resized portion of original image into new image */
1712 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1714 /* needs the image to be flipped horizontal? */
1716 $this->_debug("(FLIP)");
1717 $dst_img = $this->flipImage($dst_img, 'hori');
1719 /* needs the image to be flipped vertical? */
1721 $this->_debug("(FLIP)");
1722 $dst_img = $this->flipImage($dst_img, 'vert');
1726 $this->_debug("(ROTATE)");
1727 $dst_img = $this->rotateImage($dst_img, $rotate);
1730 /* write down new generated file */
1731 $result = imagejpeg($dst_img, $thumb_image, 75);
1733 /* free your mind */
1734 imagedestroy($dst_img);
1735 imagedestroy($src_img);
1737 if($result === false) {
1738 print "Can't write thumbnail ". $thumb_image ."\n";
1748 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1750 /* needs the image to be flipped horizontal? */
1752 $this->_debug("(FLIP)");
1753 $src_img->rotateImage(new ImagickPixel(), 90);
1754 $src_img->flipImage();
1755 $src_img->rotateImage(new ImagickPixel(), -90);
1757 /* needs the image to be flipped vertical? */
1759 $this->_debug("(FLIP)");
1760 $src_img->flipImage();
1764 $this->_debug("(ROTATE)");
1765 $src_img->rotateImage(new ImagickPixel(), $rotate);
1768 $src_img->setCompressionQuality(75);
1769 $src_img->setImageFormat('jpeg');
1771 if(!$src_img->writeImage($thumb_image)) {
1772 print "Can't write thumbnail ". $thumb_image ."\n";
1777 $src_img->destroy();
1784 } // create_thumbnail()
1787 * return all exif meta data from the file
1788 * @param string $file
1791 public function get_meta_informations($file)
1793 return exif_read_data($file);
1795 } // get_meta_informations()
1798 * create phpfspot own sqlite database
1800 * this function creates phpfspots own sqlite database
1801 * if it does not exist yet. this own is used to store
1802 * some necessary informations (md5 sum's, ...).
1804 public function check_config_table()
1806 // if the config table doesn't exist yet, create it
1807 if(!$this->cfg_db->db_check_table_exists("images")) {
1808 $this->cfg_db->db_exec("
1809 CREATE TABLE images (
1810 img_idx int primary key,
1816 } // check_config_table
1819 * Generates a thumbnail from photo idx
1821 * This function will generate JPEG thumbnails from provided F-Spot photo
1824 * 1. Check if all thumbnail generations (width) are already in place and
1826 * 2. Check if the md5sum of the original file has changed
1827 * 3. Generate the thumbnails if needed
1828 * @param integer $idx
1829 * @param integer $force
1830 * @param boolean $overwrite
1832 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1836 $resolutions = Array(
1837 $this->cfg->thumb_width,
1838 $this->cfg->photo_width,
1839 $this->cfg->mini_width,
1843 /* get details from F-Spot's database */
1844 $details = $this->get_photo_details($idx);
1846 /* calculate file MD5 sum */
1847 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1849 if(!file_exists($full_path)) {
1850 $this->_error("File ". $full_path ." does not exist\n");
1854 if(!is_readable($full_path)) {
1855 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1859 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1861 /* If Nikon NEF format, we need to treat it another way */
1862 if(isset($this->cfg->dcraw_bin) &&
1863 file_exists($this->cfg->dcraw_bin) &&
1864 is_executable($this->cfg->dcraw_bin) &&
1865 preg_match('/\.nef$/i', $details['uri'])) {
1867 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1869 /* if PPM file does not exist, let dcraw convert it from NEF */
1870 if(!file_exists($ppm_path)) {
1871 system($this->cfg->dcraw_bin ." -a ". $full_path);
1874 /* for now we handle the PPM instead of the NEF */
1875 $full_path = $ppm_path;
1879 $file_md5 = md5_file($full_path);
1882 foreach($resolutions as $resolution) {
1884 $generate_it = false;
1886 $thumb_sub_path = substr($file_md5, 0, 2);
1887 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1889 /* if thumbnail-subdirectory does not exist yet, create it */
1890 if(!file_exists(dirname($thumb_path))) {
1891 mkdir(dirname($thumb_path), 0755);
1894 /* if the thumbnail file doesn't exist, create it */
1895 if(!file_exists($thumb_path)) {
1896 $generate_it = true;
1898 /* if the file hasn't changed there is no need to regen the thumb */
1899 elseif($file_md5 != $this->getMD5($idx) || $force) {
1900 $generate_it = true;
1903 if($generate_it || $overwrite) {
1905 $this->_debug(" ". $resolution ."px");
1906 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1914 $this->_debug(" already exist");
1917 /* set the new/changed MD5 sum for the current photo */
1919 $this->setMD5($idx, $file_md5);
1922 $this->_debug("\n");
1927 * returns stored md5 sum for a specific photo
1929 * this function queries the phpfspot database for a
1930 * stored MD5 checksum of the specified photo
1931 * @param integer $idx
1932 * @return string|null
1934 public function getMD5($idx)
1936 $result = $this->cfg_db->db_query("
1939 WHERE img_idx='". $idx ."'
1945 $img = $this->cfg_db->db_fetch_object($result);
1946 return $img['img_md5'];
1951 * set MD5 sum for the specific photo
1952 * @param integer $idx
1953 * @param string $md5
1955 private function setMD5($idx, $md5)
1957 $result = $this->cfg_db->db_exec("
1958 REPLACE INTO images (img_idx, img_md5)
1959 VALUES ('". $idx ."', '". $md5 ."')
1965 * store current tag condition
1967 * this function stores the current tag condition
1968 * (AND or OR) in the users session variables
1969 * @param string $mode
1972 public function setTagCondition($mode)
1974 $_SESSION['tag_condition'] = $mode;
1978 } // setTagCondition()
1981 * invoke tag & date search
1983 * this function will return all matching tags and store
1984 * them in the session variable selected_tags. furthermore
1985 * it also handles the date search.
1986 * getPhotoSelection() will then only return the matching
1990 public function startSearch()
1993 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1994 $from = $_POST['from'];
1996 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
2000 /* tag-name search */
2001 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2002 $searchfor_tag = $_POST['for_tag'];
2003 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2006 unset($_SESSION['searchfor_tag']);
2009 /* file-name search */
2010 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2011 $_SESSION['searchfor_name'] = $_POST['for_name'];
2014 unset($_SESSION['searchfor_name']);
2018 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2020 $_SESSION['rate_from'] = $_POST['rate_from'];
2022 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2023 $_SESSION['rate_to'] = $_POST['rate_to'];
2027 /* delete any previously set value */
2028 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2033 if(isset($from) && !empty($from))
2034 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
2036 unset($_SESSION['from_date']);
2038 if(isset($to) && !empty($to))
2039 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
2041 unset($_SESSION['to_date']);
2043 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2044 /* new search, reset the current selected tags */
2045 $_SESSION['selected_tags'] = Array();
2046 foreach($this->avail_tags as $tag) {
2047 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2048 array_push($_SESSION['selected_tags'], $tag);
2057 * updates sort order in session variable
2059 * this function is invoked by RPC and will sort the requested
2060 * sort order in the session variable.
2061 * @param string $sort_order
2064 public function updateSortOrder($order)
2066 if(isset($this->sort_orders[$order])) {
2067 $_SESSION['sort_order'] = $order;
2071 return "unkown error";
2073 } // updateSortOrder()
2078 * this function rotates the image according the
2080 * @param string $img
2081 * @param integer $degress
2084 private function rotateImage($img, $degrees)
2086 if(function_exists("imagerotate")) {
2087 $img = imagerotate($img, $degrees, 0);
2089 function imagerotate($src_img, $angle)
2091 $src_x = imagesx($src_img);
2092 $src_y = imagesy($src_img);
2093 if ($angle == 180) {
2097 elseif ($src_x <= $src_y) {
2101 elseif ($src_x >= $src_y) {
2106 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2107 imagealphablending($rotate, false);
2112 for ($y = 0; $y < ($src_y); $y++) {
2113 for ($x = 0; $x < ($src_x); $x++) {
2114 $color = imagecolorat($src_img, $x, $y);
2115 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2121 for ($y = 0; $y < ($src_y); $y++) {
2122 for ($x = 0; $x < ($src_x); $x++) {
2123 $color = imagecolorat($src_img, $x, $y);
2124 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2130 for ($y = 0; $y < ($src_y); $y++) {
2131 for ($x = 0; $x < ($src_x); $x++) {
2132 $color = imagecolorat($src_img, $x, $y);
2133 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2147 $img = imagerotate($img, $degrees);
2156 * returns flipped image
2158 * this function will return an either horizontal or
2159 * vertical flipped truecolor image.
2160 * @param string $image
2161 * @param string $mode
2164 private function flipImage($image, $mode)
2166 $w = imagesx($image);
2167 $h = imagesy($image);
2168 $flipped = imagecreatetruecolor($w, $h);
2172 for ($y = 0; $y < $h; $y++) {
2173 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2177 for ($x = 0; $x < $w; $x++) {
2178 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2188 * return all assigned tags for the specified photo
2189 * @param integer $idx
2192 private function get_photo_tags($idx)
2194 $result = $this->db->db_query("
2197 INNER JOIN photo_tags pt
2199 WHERE pt.photo_id='". $idx ."'
2204 while($row = $this->db->db_fetch_object($result)) {
2205 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2207 $tags[$row['id']] = $row['name'];
2212 } // get_photo_tags()
2215 * create on-the-fly images with text within
2216 * @param string $txt
2217 * @param string $color
2218 * @param integer $space
2219 * @param integer $font
2222 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2224 if (strlen($color) != 6)
2227 $int = hexdec($color);
2228 $h = imagefontheight($font);
2229 $fw = imagefontwidth($font);
2230 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2231 $lines = count($txt);
2232 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2233 $bg = imagecolorallocate($im, 255, 255, 255);
2234 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2237 foreach ($txt as $text) {
2238 $x = (($w - ($fw * strlen($text))) / 2);
2239 imagestring($im, $font, $x, $y, $text, $color);
2240 $y += ($h + $space);
2243 Header("Content-type: image/png");
2246 } // showTextImage()
2249 * check if all requirements are met
2252 private function check_requirements()
2254 if(!function_exists("imagecreatefromjpeg")) {
2255 print "PHP GD library extension is missing<br />\n";
2259 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2260 print "PHP SQLite3 library extension is missing<br />\n";
2264 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2265 ini_set('track_errors', 1);
2266 @include_once 'HTML/AJAX/Server.php';
2267 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2268 print "PEAR HTML_AJAX package is missing<br />\n";
2271 @include_once 'Calendar/Calendar.php';
2272 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2273 print "PEAR Calendar package is missing<br />\n";
2276 @include_once 'Console/Getopt.php';
2277 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2278 print "PEAR Console_Getopt package is missing<br />\n";
2281 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2282 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2283 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2286 ini_restore('track_errors');
2293 } // check_requirements()
2295 private function _debug($text)
2297 if(isset($this->fromcmd)) {
2304 * check if specified MIME type is supported
2305 * @param string $mime
2308 public function checkifImageSupported($mime)
2310 $supported_types = Array(
2313 "image/x-portable-pixmap",
2317 if(in_array($mime, $supported_types))
2322 } // checkifImageSupported()
2326 * @param string $text
2328 public function _error($text)
2330 switch($this->cfg->logging) {
2333 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2334 print $text ."<br />\n";
2340 error_log($text, 3, $his->cfg->log_file);
2344 $this->runtime_error = true;
2349 * output calendard input fields
2350 * @param string $mode
2353 private function get_calendar($mode)
2355 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2356 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2357 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2359 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2360 if(!isset($_SESSION[$mode .'_date']))
2361 $output.= " disabled=\"disabled\"";
2363 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2364 if(!isset($_SESSION[$mode .'_date']))
2365 $output.= " disabled=\"disabled\"";
2367 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2368 if(!isset($_SESSION[$mode .'_date']))
2369 $output.= " disabled=\"disabled\"";
2377 * output calendar matrix
2378 * @param integer $year
2379 * @param integer $month
2380 * @param integer $day
2382 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2384 if (!isset($year)) $year = date('Y');
2385 if (!isset($month)) $month = date('m');
2386 if (!isset($day)) $day = date('d');
2391 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2392 require_once CALENDAR_ROOT.'Day.php';
2395 $month = new Calendar_Month_Weekdays($year,$month);
2398 $prevStamp = $month->prevMonth(true);
2399 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2400 $nextStamp = $month->nextMonth(true);
2401 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2403 $selectedDays = array (
2404 new Calendar_Day($year,$month,$day),
2405 new Calendar_Day($year,12,25),
2408 // Build the days in the month
2409 $month->build($selectedDays);
2411 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2412 $this->tmpl->assign('prev_month', $prev);
2413 $this->tmpl->assign('next_month', $next);
2415 while ( $day = $month->fetch() ) {
2417 if(!isset($matrix[$rows]))
2418 $matrix[$rows] = Array();
2422 $dayStamp = $day->thisDay(true);
2423 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2425 // isFirst() to find start of week
2426 if ( $day->isFirst() )
2429 if ( $day->isSelected() ) {
2430 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2431 } else if ( $day->isEmpty() ) {
2432 $string.= "<td> </td>\n";
2434 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2437 // isLast() to find end of week
2438 if ( $day->isLast() )
2439 $string.= "</tr>\n";
2441 $matrix[$rows][$cols] = $string;
2451 $this->tmpl->assign('matrix', $matrix);
2452 $this->tmpl->assign('rows', $rows);
2453 $this->tmpl->show("calendar.tpl");
2455 } // get_calendar_matrix()
2458 * output export page
2459 * @param string $mode
2461 public function getExport($mode)
2463 $pictures = $this->getPhotoSelection();
2464 $current_tags = $this->getCurrentTags();
2466 foreach($pictures as $picture) {
2468 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2469 if($current_tags != "") {
2470 $orig_url.= "&tags=". $current_tags;
2472 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2473 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2476 if($this->is_user_friendly_url()) {
2477 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2480 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2486 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2487 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2491 // "[%pictureurl% %thumbnailurl%]"
2492 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2495 case 'MoinMoinList':
2496 // " * [%pictureurl% %thumbnailurl%]"
2497 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2508 public function getRSSFeed()
2510 Header("Content-type: text/xml; charset=utf-8");
2511 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2514 xmlns:media="http://search.yahoo.com/mrss/"
2515 xmlns:dc="http://purl.org/dc/elements/1.1/"
2518 <title>phpfspot</title>
2519 <description>phpfspot RSS feed</description>
2520 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2521 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2522 <generator>phpfspot</generator>
2525 $pictures = $this->getPhotoSelection();
2526 $current_tags = $this->getCurrentTags();
2528 foreach($pictures as $picture) {
2530 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2531 if($current_tags != "") {
2532 $orig_url.= "&tags=". $current_tags;
2534 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2535 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2538 $details = $this->get_photo_details($picture);
2540 if($this->is_user_friendly_url()) {
2541 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2544 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2547 $thumb_html = htmlspecialchars("
2548 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2550 ". $details['description']);
2552 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2554 /* get EXIF information if JPEG */
2555 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2556 $meta = $this->get_meta_informations($orig_path);
2559 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2563 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2564 <link><?php print htmlspecialchars($orig_url); ?></link>
2565 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2566 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2568 <?php print $thumb_html; ?>
2570 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2585 * get all selected tags
2587 * This function will return all selected tags as one string, seperated
2591 private function getCurrentTags()
2594 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2595 foreach($_SESSION['selected_tags'] as $tag)
2596 $current_tags.= $tag .",";
2597 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2599 return $current_tags;
2601 } // getCurrentTags()
2604 * return the current photo
2606 public function getCurrentPhoto()
2608 if(isset($_SESSION['current_photo'])) {
2609 print $_SESSION['current_photo'];
2611 } // getCurrentPhoto()
2614 * tells the client browser what to do
2616 * this function is getting called via AJAX by the
2617 * client browsers. it will tell them what they have
2618 * to do next. This is necessary for directly jumping
2619 * into photo index or single photo view when the are
2620 * requested with specific URLs
2623 public function whatToDo()
2625 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2627 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2628 return "showpi_tags";
2630 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2637 * return the current process-user
2640 private function getuid()
2642 if($uid = posix_getuid()) {
2643 if($user = posix_getpwuid($uid)) {
2644 return $user['name'];
2653 * returns a select-dropdown box to select photo index sort parameters
2654 * @param array $params
2655 * @param smarty $smarty
2658 public function smarty_sort_select_list($params, &$smarty)
2662 foreach($this->sort_orders as $key => $value) {
2663 $output.= "<option value=\"". $key ."\"";
2664 if($key == $_SESSION['sort_order']) {
2665 $output.= " selected=\"selected\"";
2667 $output.= ">". $value ."</option>";
2672 } // smarty_sort_select_list()
2675 * returns the currently selected sort order
2678 private function get_sort_order()
2680 switch($_SESSION['sort_order']) {
2682 return " ORDER BY p.time ASC";
2685 return " ORDER BY p.time DESC";
2688 if($this->dbver < 9) {
2689 return " ORDER BY p.name ASC";
2692 return " ORDER BY basename(p.uri) ASC";
2696 if($this->dbver < 9) {
2697 return " ORDER BY p.name DESC";
2700 return " ORDER BY basename(p.uri) DESC";
2704 return " ORDER BY t.name ASC ,p.time ASC";
2707 return " ORDER BY t.name DESC ,p.time ASC";
2710 return " ORDER BY t.name ASC, p.rating ASC";
2713 return " ORDER BY t.name DESC, p.rating DESC";
2717 } // get_sort_order()
2720 * return the next to be shown slide show image
2722 * this function returns the URL of the next image
2723 * in the slideshow sequence.
2726 public function getNextSlideShowImage()
2728 $all_photos = $this->getPhotoSelection();
2730 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2731 $_SESSION['slideshow_img'] = 0;
2733 $_SESSION['slideshow_img']++;
2735 if($this->is_user_friendly_url()) {
2736 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2739 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2741 } // getNextSlideShowImage()
2744 * return the previous to be shown slide show image
2746 * this function returns the URL of the previous image
2747 * in the slideshow sequence.
2750 public function getPrevSlideShowImage()
2752 $all_photos = $this->getPhotoSelection();
2754 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2755 $_SESSION['slideshow_img'] = 0;
2757 $_SESSION['slideshow_img']--;
2759 if($this->is_user_friendly_url()) {
2760 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2763 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2765 } // getPrevSlideShowImage()
2767 public function resetSlideShow()
2769 if(isset($_SESSION['slideshow_img']))
2770 unset($_SESSION['slideshow_img']);
2772 } // resetSlideShow()
2777 * this function will get all photos from the fspot
2778 * database and randomly return ONE entry
2780 * saddly there is yet no sqlite3 function which returns
2781 * the bulk result in array, so we have to fill up our
2785 public function get_random_photo()
2794 /* if show_tags is set, only return details for photos which
2795 are specified to be shown
2797 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2799 INNER JOIN photo_tags pt
2804 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2807 $result = $this->db->db_query($query_str);
2809 while($row = $this->db->db_fetch_object($result)) {
2810 array_push($all, $row['id']);
2813 return $all[array_rand($all)];
2815 } // get_random_photo()
2818 * get random photo tag photo
2820 * this function will get all photos tagged with the requested
2821 * tag from the fspot database and randomly return ONE entry
2823 * saddly there is yet no sqlite3 function which returns
2824 * the bulk result in array, so we have to fill up our
2828 public function get_random_tag_photo($tagidx)
2835 INNER JOIN photo_tags pt
2839 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2847 pt.tag_id LIKE '". $tagidx ."'
2850 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2853 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2857 $result = $this->db->db_query($query_str);
2859 while($row = $this->db->db_fetch_object($result)) {
2860 array_push($all, $row['id']);
2863 return $all[array_rand($all)];
2865 } // get_random_tag_photo()
2868 * validates provided date
2870 * this function validates if the provided date
2871 * contains a valid date and will return true
2873 * @param string $date_str
2876 public function isValidDate($date_str)
2878 $timestamp = strtotime($date_str);
2880 if(is_numeric($timestamp))
2888 * timestamp to string conversion
2889 * @param integer $timestamp
2892 private function ts2str($timestamp)
2894 if(!empty($timestamp) && is_numeric($timestamp))
2895 return strftime("%Y-%m-%d", $timestamp);
2900 * extract tag-names from $_GET['tags']
2901 * @param string $tags_str
2904 private function extractTags($tags_str)
2906 $not_validated = split(',', $tags_str);
2907 $validated = array();
2909 foreach($not_validated as $tag) {
2910 if(is_numeric($tag))
2911 array_push($validated, $tag);
2919 * returns the full path to a thumbnail
2920 * @param integer $width
2921 * @param integer $photo
2924 public function get_thumb_path($width, $photo)
2926 $md5 = $this->getMD5($photo);
2927 $sub_path = substr($md5, 0, 2);
2928 return $this->cfg->thumb_path
2936 } // get_thumb_path()
2939 * returns server's virtual host name
2942 private function get_server_name()
2944 return $_SERVER['SERVER_NAME'];
2945 } // get_server_name()
2948 * returns type of webprotocol which is currently used
2951 private function get_web_protocol()
2953 if(!isset($_SERVER['HTTPS']))
2957 } // get_web_protocol()
2960 * return url to this phpfspot installation
2963 private function get_phpfspot_url()
2965 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2967 } // get_phpfspot_url()
2970 * returns the number of photos which are tagged with $tag_id
2971 * @param integer $tag_id
2974 public function get_num_photos($tag_id)
2976 if($result = $this->db->db_fetchSingleRow("
2977 SELECT count(*) as number
2980 tag_id LIKE '". $tag_id ."'")) {
2982 return $result['number'];
2988 } // get_num_photos()
2991 * check file exists and is readable
2993 * returns true, if everything is ok, otherwise false
2994 * if $silent is not set, this function will output and
2996 * @param string $file
2997 * @param boolean $silent
3000 private function check_readable($file, $silent = null)
3002 if(!file_exists($file)) {
3004 print "File \"". $file ."\" does not exist.\n";
3008 if(!is_readable($file)) {
3010 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3016 } // check_readable()
3019 * check if all needed indices are present
3021 * this function checks, if some needed indices are already
3022 * present, or if not, create them on the fly. they are
3023 * necessary to speed up some queries like that one look for
3024 * all tags, when show_tags is specified in the configuration.
3026 private function checkDbIndices()
3028 $result = $this->db->db_exec("
3029 CREATE INDEX IF NOT EXISTS
3036 } // checkDbIndices()
3039 * retrive F-Spot database version
3041 * this function will return the F-Spot database version number
3042 * It is stored within the sqlite3 database in the table meta
3043 * @return string|null
3045 public function getFspotDBVersion()
3047 if($result = $this->db->db_fetchSingleRow("
3048 SELECT data as version
3051 name LIKE 'F-Spot Database Version'
3053 return $result['version'];
3057 } // getFspotDBVersion()
3060 * parse the provided URI and will returned the requested chunk
3061 * @param string $uri
3062 * @param string $mode
3065 public function parse_uri($uri, $mode)
3067 if(($components = parse_url($uri)) !== false) {
3071 return basename($components['path']);
3074 return dirname($components['path']);
3077 return $components['path'];
3087 * validate config options
3089 * this function checks if all necessary configuration options are
3090 * specified and set.
3093 private function check_config_options()
3095 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3096 $this->_error("Please set \$page_title in phpfspot_cfg");
3098 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3099 $this->_error("Please set \$base_path in phpfspot_cfg");
3101 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3102 $this->_error("Please set \$web_path in phpfspot_cfg");
3104 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3105 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3107 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3108 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3110 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3111 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3113 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3114 $this->_error("Please set \$db_access in phpfspot_cfg");
3116 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3117 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3119 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3120 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3122 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3123 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3125 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3126 $this->_error("Please set \$photo_width in phpfspot_cfg");
3128 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3129 $this->_error("Please set \$mini_width in phpfspot_cfg");
3131 if(!isset($this->cfg->thumbs_per_page))
3132 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3134 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3135 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3137 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3138 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3140 if(!isset($this->cfg->hide_tags))
3141 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3143 if(!isset($this->cfg->theme_name))
3144 $this->_error("Please set \$theme_name in phpfspot_cfg");
3146 if(!isset($this->cfg->logging))
3147 $this->_error("Please set \$logging in phpfspot_cfg");
3149 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3151 if(!isset($this->cfg->log_file))
3152 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3154 if(!is_writeable($this->cfg->log_file))
3155 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3159 /* remove trailing slash, if set */
3160 if($this->cfg->web_path == "/")
3161 $this->cfg->web_path = "";
3162 elseif(preg_match('/\/$/', $this->cfg->web_path))
3163 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3165 return $this->runtime_error;
3167 } // check_config_options()
3170 * cleanup phpfspot own database
3172 * When photos are getting delete from F-Spot, there will remain
3173 * remain some residues in phpfspot own database. This function
3174 * will try to wipe them out.
3176 public function cleanup_phpfspot_db()
3178 $to_delete = Array();
3180 $result = $this->cfg_db->db_query("
3183 ORDER BY img_idx ASC
3186 while($row = $this->cfg_db->db_fetch_object($result)) {
3187 if(!$this->db->db_fetchSingleRow("
3190 WHERE id='". $row['img_idx'] ."'")) {
3192 array_push($to_delete, $row['img_idx'], ',');
3196 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3198 $this->cfg_db->db_exec("
3200 WHERE img_idx IN (". implode($to_delete) .")
3203 } // cleanup_phpfspot_db()
3206 * return first image of the page, the $current photo
3209 * this function is used to find out the first photo of the
3210 * current page, in which the $current photo lies. this is
3211 * used to display the correct photo, when calling showPhotoIndex()
3213 * @param integer $current
3214 * @param integer $max
3217 private function getCurrentPage($current, $max)
3219 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3220 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3221 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3227 } // getCurrentPage()
3232 * this function tries to find out the correct mime-type
3233 * for the provided file.
3234 * @param string $file
3237 public function get_mime_info($file)
3239 $details = getimagesize($file);
3241 /* if getimagesize() returns empty, try at least to find out the
3244 if(empty($details) && function_exists('mime_content_type')) {
3246 // mime_content_type is marked as deprecated in the documentation,
3247 // but is it really necessary to force users to install a PECL
3249 $details['mime'] = mime_content_type($file);
3252 return $details['mime'];
3254 } // get_mime_info()
3257 * return tag-name by tag-idx
3259 * this function returns the tag-name for the requested
3260 * tag specified by tag-idx.
3261 * @param integer $idx
3264 public function get_tag_name($idx)
3266 if($result = $this->db->db_fetchSingleRow("
3270 id LIKE '". $idx ."'")) {
3272 return $result['name'];
3281 * parse user friendly url which got rewritten by the websever
3282 * @param string $request_uri
3285 private function parse_user_friendly_url($request_uri)
3287 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3289 $options = explode('/', $request_uri);
3291 switch($options[1]) {
3293 if(is_numeric($options[2])) {
3294 $this->session_cleanup();
3295 //unset($_SESSION['start_action']);
3296 //unset($_SESSION['selected_tags']);
3297 $_GET['mode'] = 'showp';
3298 return $this->showPhoto($options[2]);
3302 if(is_numeric($options[2])) {
3303 require_once "phpfspot_img.php";
3304 $img = new PHPFSPOT_IMG;
3305 if(isset($options[3]) && is_numeric($options[3]))
3306 $img->showImg($options[2], $options[3]);
3308 $img->showImg($options[2]);
3313 if(is_numeric($options[2])) {
3314 $this->session_cleanup();
3315 $_GET['tags'] = $options[2];
3316 $_SESSION['selected_tags'] = Array($options[2]);
3317 if(isset($options[3]) && is_numeric($options[3]))
3318 $_SESSION['begin_with'] = $options[3];
3319 return $this->showPhotoIndex();
3325 } // parse_user_friendly_url()
3328 * check if user-friendly-urls are enabled
3330 * this function will return true, if the config option
3331 * $user_friendly_url has been set. Otherwise false.
3334 private function is_user_friendly_url()
3336 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3341 } // is_user_friendly_url()
3346 * this function will cleanup user's session information
3348 private function session_cleanup()
3350 unset($_SESSION['begin_with']);
3351 $this->resetDateSearch();
3352 $this->resetPhotoView();
3353 $this->resetTagSearch();
3354 $this->resetNameSearch();
3355 $this->resetDateSearch();
3358 } // session_cleanup()