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 /* fetch export template */
303 print $this->tmpl->fetch("export.tpl");
304 /* no further execution necessary. */
308 /* fetch slideshow template */
309 print $this->tmpl->show("slideshow.tpl");
310 /* no further execution necessary. */
314 if(isset($_GET['tags'])) {
315 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
317 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
318 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
320 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
321 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
329 /* if date-search variables are registered in the session, set the check
330 for "consider date-range" in the html output
332 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
333 $this->tmpl->assign('date_search_enabled', true);
335 /* if rate-search variables are registered in the session, set the check
336 for "consider rate-range" in the html output
338 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
339 $this->tmpl->assign('rate_search_enabled', true);
342 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
343 $this->tmpl->assign('search_from_date', $this->get_calendar('from'));
344 $this->tmpl->assign('search_to_date', $this->get_calendar('to'));
346 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
347 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
348 $this->tmpl->assign('rate_search', $this->get_rate_search());
350 /* if no site-content has been set yet... */
351 if(!isset($content)) {
352 /* if tags are already selected, we can immediately display photo-index */
353 if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) &&
354 $_SESSION['start_action'] != 'showp') || $_SESSION['start_action'] == 'showpi')
355 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
357 /* if a photo is already selected, we can immediately display single-photo */
358 if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo']))
359 $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo']));
361 /* ok, then let us show the welcome page... */
362 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
367 $this->tmpl->assign('initial_content', $content);
369 $this->tmpl->show("index.tpl");
374 * get_tags - grab all tags of f-spot's database
376 * this function will get all available tags from
377 * the f-spot database and store them within two
378 * arrays within this class for later usage. in
379 * fact, if the user requests (hide_tags) it will
380 * opt-out some of them.
382 * this function is getting called once by show()
384 private function get_tags()
386 $this->avail_tags = Array();
389 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
392 DISTINCT t1.id as id, t1.name as name
395 INNER JOIN photo_tags
396 pt2 ON pt1.photo_id=pt2.photo_id
402 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
404 t1.sort_priority ASC";
406 $result = $this->db->db_query($query_str);
410 $result = $this->db->db_query("
413 ORDER BY sort_priority ASC
417 while($row = $this->db->db_fetch_object($result)) {
419 $tag_id = $row['id'];
420 $tag_name = $row['name'];
422 /* if the user has specified to ignore this tag in phpfspot's
423 configuration, ignore it here so it does not get added to
426 if(in_array($row['name'], $this->cfg->hide_tags))
429 /* if you include the following if-clause and the user has specified
430 to only show certain tags which are specified in phpfspot's
431 configuration, ignore all others so they will not be added to the
433 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
434 !in_array($row['name'], $this->cfg->show_tags))
438 $this->tags[$tag_id] = $tag_name;
439 $this->avail_tags[$count] = $tag_id;
447 * extract all photo details
449 * retrieve all available details from f-spot's
450 * database and return them as object
451 * @param integer $idx
452 * @return object|null
454 public function get_photo_details($idx)
456 if($this->dbver < 9) {
458 SELECT p.id, p.name, p.time, p.directory_path, p.description
463 /* till F-Spot version 0.4.1 */
464 if($this->dbver < 11) {
466 SELECT p.id, p.uri, p.time, p.description
472 SELECT p.id, p.uri, p.time, p.description, p.rating
478 /* if show_tags is set, only return details for photos which
479 are specified to be shown
481 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
483 INNER JOIN photo_tags pt
487 WHERE p.id='". $idx ."'
488 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
492 WHERE p.id='". $idx ."'
496 if($result = $this->db->db_query($query_str)) {
498 $row = $this->db->db_fetch_object($result);
500 if($this->dbver < 9) {
501 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
510 } // get_photo_details
513 * returns aligned photo names
515 * this function returns aligned (length) names for
516 * an specific photo. If the length of the name exceeds
517 * $limit the name will be shrinked (...)
518 * @param integer $idx
519 * @param integer $limit
520 * @return string|null
522 public function getPhotoName($idx, $limit = 0)
524 if($details = $this->get_photo_details($idx)) {
525 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
526 $name = $this->shrink_text($long_name, $limit);
536 * get photo rating level
538 * this function will return the integer-based rating
539 * level of the photo. This can only be done, if the F-Spot
540 * database is at a specific level. If rating value can not
541 * be found, zero will be returned indicating no rating value
546 public function get_photo_rating($idx)
548 if($detail = $this->get_photo_details($idx)) {
549 if(isset($detail['rating']))
550 return $detail['rating'];
555 } // get_photo_rating()
558 * get rate-search bars
560 * this function will return the rating-bars for the
564 public function get_rate_search()
568 for($i = 1; $i <= 5; $i++) {
570 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
572 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
573 $bar.= $this->cfg->web_path ."/resources/star.png";
575 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
578 onmouseover=\"show_rate('from', ". $i .");\"
579 onmouseout=\"reset_rate('from');\"
580 onclick=\"set_rate('from', ". $i .")\" />";
585 for($i = 1; $i <= 5; $i++) {
587 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
589 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
590 $bar.= $this->cfg->web_path ."/resources/star.png";
592 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
595 onmouseover=\"show_rate('to', ". $i .");\"
596 onmouseout=\"reset_rate('to');\"
597 onclick=\"set_rate('to', ". $i .");\" />";
602 } // get_rate_search()
605 * shrink text according provided limit
607 * If the length of the name exceeds $limit the
608 * text will be shortend and some content in between
609 * will be replaced with "..."
611 * @param integer $limit
614 private function shrink_text($text, $limit)
616 if($limit != 0 && strlen($text) > $limit) {
617 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
625 * translate f-spoth photo path
627 * as the full-qualified path recorded in the f-spot database
628 * is usally not the same as on the webserver, this function
629 * will replace the path with that one specified in the cfg
630 * @param string $path
633 public function translate_path($path)
635 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
640 * control HTML ouput for a single photo
642 * this function provides all the necessary information
643 * for the single photo template.
644 * @param integer photo
646 public function showPhoto($photo)
648 /* get all photos from the current photo selection */
649 $all_photos = $this->getPhotoSelection();
650 $count = count($all_photos);
652 for($i = 0; $i < $count; $i++) {
654 // $get_next will be set, when the photo which has to
655 // be displayed has been found - this means that the
656 // next available is in fact the NEXT image (for the
658 if(isset($get_next)) {
659 $next_img = $all_photos[$i];
663 /* the next photo is our NEXT photo */
664 if($all_photos[$i] == $photo) {
668 $previous_img = $all_photos[$i];
671 if($photo == $all_photos[$i]) {
676 $details = $this->get_photo_details($photo);
683 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
684 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
686 if(!file_exists($orig_path)) {
687 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
691 if(!is_readable($orig_path)) {
692 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
696 /* If the thumbnail doesn't exist yet, try to create it */
697 if(!file_exists($thumb_path)) {
698 $this->gen_thumb($photo, true);
699 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
702 /* get mime-type, height and width from the original photo */
703 $info = getimagesize($orig_path);
705 /* get EXIF information if JPEG */
706 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
707 $meta = $this->get_meta_informations($orig_path);
710 /* If EXIF data are available, use them */
711 if(isset($meta['ExifImageWidth'])) {
712 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
714 $meta_res = $info[0] ."x". $info[1];
717 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
718 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
719 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
721 $extern_link = "index.php?mode=showp&id=". $photo;
722 $current_tags = $this->getCurrentTags();
723 if($current_tags != "") {
724 $extern_link.= "&tags=". $current_tags;
726 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
727 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
730 $this->tmpl->assign('extern_link', $extern_link);
732 if(!file_exists($thumb_path)) {
733 $this->_error("Can't open file ". $thumb_path ."\n");
737 $info_thumb = getimagesize($thumb_path);
739 $this->tmpl->assign('description', $details['description']);
740 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
741 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
743 $this->tmpl->assign('width', $info_thumb[0]);
744 $this->tmpl->assign('height', $info_thumb[1]);
745 $this->tmpl->assign('ExifMadeOn', $meta_date);
746 $this->tmpl->assign('ExifMadeWith', $meta_make);
747 $this->tmpl->assign('ExifOrigResolution', $meta_res);
748 $this->tmpl->assign('ExifFileSize', $meta_size);
750 if($this->is_user_friendly_url()) {
751 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
752 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
755 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
756 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
759 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
761 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
762 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
763 $this->tmpl->assign('current_img', $photo);
765 if(isset($previous_img)) {
766 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
767 $this->tmpl->assign('prev_img', $previous_img);
770 if(isset($next_img)) {
771 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
772 $this->tmpl->assign('next_img', $next_img);
775 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
776 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
777 $this->tmpl->assign('photo_number', $i);
778 $this->tmpl->assign('photo_count', count($all_photos));
780 return $this->tmpl->fetch("single_photo.tpl");
785 * all available tags and tag cloud
787 * this function outputs all available tags (time ordered)
788 * and in addition output them as tag cloud (tags which have
789 * many photos will appears more then others)
791 public function getAvailableTags()
793 /* retrive tags from database */
798 $result = $this->db->db_query("
799 SELECT tag_id as id, count(tag_id) as quantity
809 while($row = $this->db->db_fetch_object($result)) {
810 $tags[$row['id']] = $row['quantity'];
813 // change these font sizes if you will
814 $max_size = 125; // max font size in %
815 $min_size = 75; // min font size in %
818 $max_sat = hexdec('cc');
819 $min_sat = hexdec('44');
821 // get the largest and smallest array values
822 $max_qty = max(array_values($tags));
823 $min_qty = min(array_values($tags));
825 // find the range of values
826 $spread = $max_qty - $min_qty;
827 if (0 == $spread) { // we don't want to divide by zero
831 // determine the font-size increment
832 // this is the increase per tag quantity (times used)
833 $step = ($max_size - $min_size)/($spread);
834 $step_sat = ($max_sat - $min_sat)/($spread);
836 // loop through our tag array
837 foreach ($tags as $key => $value) {
839 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
842 // calculate CSS font-size
843 // find the $value in excess of $min_qty
844 // multiply by the font-size increment ($size)
845 // and add the $min_size set above
846 $size = $min_size + (($value - $min_qty) * $step);
847 // uncomment if you want sizes in whole %:
850 $color = $min_sat + ($value - $min_qty) * $step_sat;
856 if(isset($this->tags[$key])) {
857 if($this->is_user_friendly_url())
858 $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>, ";
860 $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>, ";
864 $output = substr($output, 0, strlen($output)-2);
867 } // getAvailableTags()
870 * output all selected tags
872 * this function output all tags which have been selected
873 * by the user. the selected tags are stored in the
874 * session-variable $_SESSION['selected_tags']
877 public function getSelectedTags($type = 'link')
879 /* retrive tags from database */
884 foreach($this->avail_tags as $tag)
886 // return all selected tags
887 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
892 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
896 <div class=\"tagresulttag\">
897 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
898 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
908 $output = substr($output, 0, strlen($output)-2);
912 return "no tags selected";
915 } // getSelectedTags()
918 * add tag to users session variable
920 * this function will add the specified to users current
921 * tag selection. if a date search has been made before
922 * it will be now cleared
925 public function addTag($tag)
927 if(!isset($_SESSION['selected_tags']))
928 $_SESSION['selected_tags'] = Array();
930 if(isset($_SESSION['searchfor_tag']))
931 unset($_SESSION['searchfor_tag']);
933 // has the user requested to hide this tag, and still someone,
934 // somehow tries to add it, don't allow this.
935 if(!isset($this->cfg->hide_tags) &&
936 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
939 if(!in_array($tag, $_SESSION['selected_tags']))
940 array_push($_SESSION['selected_tags'], $tag);
947 * remove tag to users session variable
949 * this function removes the specified tag from
950 * users current tag selection
954 public function delTag($tag)
956 if(isset($_SESSION['searchfor_tag']))
957 unset($_SESSION['searchfor_tag']);
959 if(isset($_SESSION['selected_tags'])) {
960 $key = array_search($tag, $_SESSION['selected_tags']);
961 unset($_SESSION['selected_tags'][$key]);
962 sort($_SESSION['selected_tags']);
970 * reset tag selection
972 * if there is any tag selection, it will be
975 public function resetTags()
977 if(isset($_SESSION['selected_tags']))
978 unset($_SESSION['selected_tags']);
983 * returns the value for the autocomplete tag-search
986 public function get_xml_tag_list()
988 if(!isset($_GET['search']) || !is_string($_GET['search']))
989 $_GET['search'] = '';
994 /* retrive tags from database */
997 $matched_tags = Array();
999 header("Content-Type: text/xml");
1001 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1002 $string.= "<results>\n";
1004 foreach($this->avail_tags as $tag)
1006 if(!empty($_GET['search']) &&
1007 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1008 count($matched_tags) < $length) {
1010 $count = $this->get_num_photos($tag);
1013 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1016 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1022 /* if we have collected enough items, break out */
1023 if(count($matched_tags) >= $length)
1027 $string.= "</results>\n";
1031 } // get_xml_tag_list()
1035 * reset single photo
1037 * if a specific photo was requested (external link)
1038 * unset the session variable now
1040 public function resetPhotoView()
1042 if(isset($_SESSION['current_photo']))
1043 unset($_SESSION['current_photo']);
1045 } // resetPhotoView();
1050 * if any tag search has taken place, reset it now
1052 public function resetTagSearch()
1054 if(isset($_SESSION['searchfor_tag']))
1055 unset($_SESSION['searchfor_tag']);
1057 } // resetTagSearch()
1062 * if any name search has taken place, reset it now
1064 public function resetNameSearch()
1066 if(isset($_SESSION['searchfor_name']))
1067 unset($_SESSION['searchfor_name']);
1069 } // resetNameSearch()
1074 * if any date search has taken place, reset it now.
1076 public function resetDateSearch()
1078 if(isset($_SESSION['from_date']))
1079 unset($_SESSION['from_date']);
1080 if(isset($_SESSION['to_date']))
1081 unset($_SESSION['to_date']);
1083 } // resetDateSearch();
1088 * if any rate search has taken place, reset it now.
1090 public function resetRateSearch()
1092 if(isset($_SESSION['rate_from']))
1093 unset($_SESSION['rate_from']);
1094 if(isset($_SESSION['rate_to']))
1095 unset($_SESSION['rate_to']);
1097 } // resetRateSearch();
1100 * return all photo according selection
1102 * this function returns all photos based on
1103 * the tag-selection, tag- or date-search.
1104 * the tag-search also has to take care of AND
1105 * and OR conjunctions
1108 public function getPhotoSelection()
1110 $matched_photos = Array();
1111 $additional_where_cond = "";
1113 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1114 $from_date = $_SESSION['from_date'];
1115 $to_date = $_SESSION['to_date'];
1116 $additional_where_cond.= "
1117 p.time>='". $from_date ."'
1119 p.time<='". $to_date ."'
1123 if(isset($_SESSION['searchfor_name'])) {
1125 /* check for previous conditions. if so add 'AND' */
1126 if(!empty($additional_where_cond)) {
1127 $additional_where_cond.= " AND ";
1130 if($this->dbver < 9) {
1131 $additional_where_cond.= "
1133 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1135 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1140 $additional_where_cond.= "
1142 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1144 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1150 /* limit result based on rate-search */
1151 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1153 if($this->dbver > 10) {
1155 /* check for previous conditions. if so add 'AND' */
1156 if(!empty($additional_where_cond)) {
1157 $additional_where_cond.= " AND ";
1160 $additional_where_cond.= "
1161 p.rating >= ". $_SESSION['rate_from'] ."
1163 p.rating <= ". $_SESSION['rate_to'] ."
1168 if(isset($_SESSION['sort_order'])) {
1169 $order_str = $this->get_sort_order();
1172 /* return a search result */
1173 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1175 SELECT DISTINCT pt1.photo_id
1177 INNER JOIN photo_tags pt2
1178 ON pt1.photo_id=pt2.photo_id
1182 ON pt1.photo_id=p.id
1185 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1187 if(!empty($additional_where_cond))
1188 $query_str.= "AND ". $additional_where_cond ." ";
1190 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1191 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1194 if(isset($order_str))
1195 $query_str.= $order_str;
1197 $result = $this->db->db_query($query_str);
1198 while($row = $this->db->db_fetch_object($result)) {
1199 array_push($matched_photos, $row['photo_id']);
1201 return $matched_photos;
1204 /* return according the selected tags */
1205 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1207 foreach($_SESSION['selected_tags'] as $tag)
1208 $selected.= $tag .",";
1209 $selected = substr($selected, 0, strlen($selected)-1);
1211 /* photo has to match at least on of the selected tags */
1212 if($_SESSION['tag_condition'] == 'or') {
1214 SELECT DISTINCT pt1.photo_id
1216 INNER JOIN photo_tags pt2
1217 ON pt1.photo_id=pt2.photo_id
1221 ON pt1.photo_id=p.id
1222 WHERE pt1.tag_id IN (". $selected .")
1224 if(!empty($additional_where_cond))
1225 $query_str.= "AND ". $additional_where_cond ." ";
1227 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1228 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1231 if(isset($order_str))
1232 $query_str.= $order_str;
1234 /* photo has to match all selected tags */
1235 elseif($_SESSION['tag_condition'] == 'and') {
1237 if(count($_SESSION['selected_tags']) >= 32) {
1238 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1239 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1243 /* Join together a table looking like
1245 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1247 so the query can quickly return all images matching the
1248 selected tags in an AND condition
1253 SELECT DISTINCT pt1.photo_id
1257 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1264 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1266 INNER JOIN photo_tags pt". ($i+2) ."
1267 ON pt1.photo_id=pt". ($i+2) .".photo_id
1272 ON pt1.photo_id=p.id
1274 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1275 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1277 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1280 if(!empty($additional_where_cond))
1281 $query_str.= "AND ". $additional_where_cond;
1283 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1284 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1287 if(isset($order_str))
1288 $query_str.= $order_str;
1292 $result = $this->db->db_query($query_str);
1293 while($row = $this->db->db_fetch_object($result)) {
1294 array_push($matched_photos, $row['photo_id']);
1296 return $matched_photos;
1299 /* return all available photos */
1301 SELECT DISTINCT p.id
1303 LEFT JOIN photo_tags pt
1309 if(!empty($additional_where_cond))
1310 $query_str.= "WHERE ". $additional_where_cond ." ";
1312 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1313 if(!empty($additional_where_cond))
1314 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1316 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1319 if(isset($order_str))
1320 $query_str.= $order_str;
1322 $result = $this->db->db_query($query_str);
1323 while($row = $this->db->db_fetch_object($result)) {
1324 array_push($matched_photos, $row['id']);
1326 return $matched_photos;
1328 } // getPhotoSelection()
1331 * control HTML ouput for photo index
1333 * this function provides all the necessary information
1334 * for the photo index template.
1337 public function showPhotoIndex()
1339 $photos = $this->getPhotoSelection();
1340 $current_tags = $this->getCurrentTags();
1342 $count = count($photos);
1344 /* if all thumbnails should be shown on one page */
1345 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1349 /* thumbnails should be splitted up in several pages */
1350 elseif($this->cfg->thumbs_per_page > 0) {
1352 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1356 $begin_with = $_SESSION['begin_with'];
1359 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1363 $images[$thumbs] = Array();
1364 $img_height[$thumbs] = Array();
1365 $img_width[$thumbs] = Array();
1366 $img_id[$thumbs] = Array();
1367 $img_name[$thumbs] = Array();
1368 $img_fullname[$thumbs] = Array();
1369 $img_title = Array();
1370 $img_rating = Array();
1372 for($i = $begin_with; $i < $end_with; $i++) {
1374 if(isset($photos[$i])) {
1376 $images[$thumbs] = $photos[$i];
1377 $img_id[$thumbs] = $i;
1378 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1379 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1380 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1381 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1383 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1385 if(file_exists($thumb_path)) {
1386 $info = getimagesize($thumb_path);
1387 $img_width[$thumbs] = $info[0];
1388 $img_height[$thumbs] = $info[1];
1394 // +1 for for smarty's selection iteration
1397 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1398 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1400 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1401 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1402 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1405 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1406 $this->tmpl->assign('tag_result', 1);
1409 /* do we have to display the page selector ? */
1410 if($this->cfg->thumbs_per_page != 0) {
1414 /* calculate the page switchers */
1415 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1416 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1418 if($begin_with != 0)
1419 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1420 if($end_with < $count)
1421 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1423 $photo_per_page = $this->cfg->thumbs_per_page;
1424 $last_page = ceil($count / $photo_per_page);
1426 /* get the current selected page */
1427 if($begin_with == 0) {
1431 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1438 for($i = 1; $i <= $last_page; $i++) {
1440 if($current_page == $i)
1441 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1442 elseif($current_page-1 == $i || $current_page+1 == $i)
1443 $style = "style=\"font-size: 105%;\"";
1444 elseif(($current_page-5 >= $i) && ($i != 1) ||
1445 ($current_page+5 <= $i) && ($i != $last_page))
1446 $style = "style=\"font-size: 75%;\"";
1450 $start_with = ($i*$photo_per_page)-$photo_per_page;
1452 if($this->is_user_friendly_url()) {
1453 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1456 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1458 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1462 $select.= ">". $i ."</a> ";
1464 // until 9 pages we show the selector from 1-9
1465 if($last_page <= 9) {
1466 $page_select.= $select;
1469 if($i == 1 /* first page */ ||
1470 $i == $last_page /* last page */ ||
1471 $i == $current_page /* current page */ ||
1472 $i == ceil($last_page * 0.25) /* first quater */ ||
1473 $i == ceil($last_page * 0.5) /* half */ ||
1474 $i == ceil($last_page * 0.75) /* third quater */ ||
1475 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1476 (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 */ ||
1477 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1478 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1480 $page_select.= $select;
1488 $page_select.= "......... ";
1493 /* only show the page selector if we have more then one page */
1495 $this->tmpl->assign('page_selector', $page_select);
1498 $extern_link = "index.php?mode=showpi";
1499 $rss_link = "index.php?mode=rss";
1500 if($current_tags != "") {
1501 $extern_link.= "&tags=". $current_tags;
1502 $rss_link.= "&tags=". $current_tags;
1504 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1505 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1506 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1509 $export_link = "index.php?mode=export";
1510 $slideshow_link = "index.php?mode=slideshow";
1512 $this->tmpl->assign('extern_link', $extern_link);
1513 $this->tmpl->assign('slideshow_link', $slideshow_link);
1514 $this->tmpl->assign('export_link', $export_link);
1515 $this->tmpl->assign('rss_link', $rss_link);
1516 $this->tmpl->assign('count', $count);
1517 $this->tmpl->assign('width', $this->cfg->thumb_width);
1518 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1519 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1520 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1521 $this->tmpl->assign('images', $images);
1522 $this->tmpl->assign('img_width', $img_width);
1523 $this->tmpl->assign('img_height', $img_height);
1524 $this->tmpl->assign('img_id', $img_id);
1525 $this->tmpl->assign('img_name', $img_name);
1526 $this->tmpl->assign('img_fullname', $img_fullname);
1527 $this->tmpl->assign('img_title', $img_title);
1528 $this->tmpl->assign('img_rating', $img_rating);
1529 $this->tmpl->assign('thumbs', $thumbs);
1530 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1532 $result = $this->tmpl->fetch("photo_index.tpl");
1534 /* if we are returning to photo index from an photo-view,
1535 scroll the window to the last shown photo-thumbnail.
1536 after this, unset the last_photo session variable.
1538 if(isset($_SESSION['last_photo'])) {
1539 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1540 unset($_SESSION['last_photo']);
1545 } // showPhotoIndex()
1548 * show credit template
1550 public function showCredits()
1552 $this->tmpl->assign('version', $this->cfg->version);
1553 $this->tmpl->assign('product', $this->cfg->product);
1554 $this->tmpl->assign('db_version', $this->dbver);
1555 $this->tmpl->show("credits.tpl");
1560 * create thumbnails for the requested width
1562 * this function creates image thumbnails of $orig_image
1563 * stored as $thumb_image. It will check if the image is
1564 * in a supported format, if necessary rotate the image
1565 * (based on EXIF orientation meta headers) and re-sizing.
1566 * @param string $orig_image
1567 * @param string $thumb_image
1568 * @param integer $width
1571 public function create_thumbnail($orig_image, $thumb_image, $width)
1573 if(!file_exists($orig_image)) {
1577 $mime = $this->get_mime_info($orig_image);
1579 /* check if original photo is a support image type */
1580 if(!$this->checkifImageSupported($mime))
1587 $meta = $this->get_meta_informations($orig_image);
1593 if(isset($meta['Orientation'])) {
1594 switch($meta['Orientation']) {
1595 case 1: /* top, left */
1596 /* nothing to do */ break;
1597 case 2: /* top, right */
1598 $rotate = 0; $flip_hori = true; break;
1599 case 3: /* bottom, left */
1600 $rotate = 180; break;
1601 case 4: /* bottom, right */
1602 $flip_vert = true; break;
1603 case 5: /* left side, top */
1604 $rotate = 90; $flip_vert = true; break;
1605 case 6: /* right side, top */
1606 $rotate = 90; break;
1607 case 7: /* left side, bottom */
1608 $rotate = 270; $flip_vert = true; break;
1609 case 8: /* right side, bottom */
1610 $rotate = 270; break;
1614 $src_img = @imagecreatefromjpeg($orig_image);
1620 $src_img = @imagecreatefrompng($orig_image);
1624 case 'image/x-portable-pixmap':
1626 $src_img = new Imagick($orig_image);
1627 $handler = "imagick";
1632 if(!isset($src_img) || empty($src_img)) {
1633 print "Can't load image from ". $orig_image ."\n";
1641 /* grabs the height and width */
1642 $cur_width = imagesx($src_img);
1643 $cur_height = imagesy($src_img);
1645 // If requested width is more then the actual image width,
1646 // do not generate a thumbnail, instead safe the original
1647 // as thumbnail but with lower quality. But if the image
1648 // is to heigh too, then we still have to resize it.
1649 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1650 $result = imagejpeg($src_img, $thumb_image, 75);
1651 imagedestroy($src_img);
1658 $cur_width = $src_img->getImageWidth();
1659 $cur_height = $src_img->getImageHeight();
1661 // If requested width is more then the actual image width,
1662 // do not generate a thumbnail, instead safe the original
1663 // as thumbnail but with lower quality. But if the image
1664 // is to heigh too, then we still have to resize it.
1665 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1666 $src_img->setCompressionQuality(75);
1667 $src_img->setImageFormat('jpeg');
1668 $src_img->writeImage($thumb_image);
1670 $src_img->destroy();
1677 // If the image will be rotate because EXIF orientation said so
1678 // 'virtually rotate' the image for further calculations
1679 if($rotate == 90 || $rotate == 270) {
1681 $cur_width = $cur_height;
1685 /* calculates aspect ratio */
1686 $aspect_ratio = $cur_height / $cur_width;
1689 if($aspect_ratio < 1) {
1691 $new_h = abs($new_w * $aspect_ratio);
1693 /* 'virtually' rotate the image and calculate it's ratio */
1694 $tmp_w = $cur_height;
1695 $tmp_h = $cur_width;
1696 /* now get the ratio from the 'rotated' image */
1697 $tmp_ratio = $tmp_h/$tmp_w;
1698 /* now calculate the new dimensions */
1700 $tmp_h = abs($tmp_w * $tmp_ratio);
1702 // now that we know, how high they photo should be, if it
1703 // gets rotated, use this high to scale the image
1705 $new_w = abs($new_h / $aspect_ratio);
1707 // If the image will be rotate because EXIF orientation said so
1708 // now 'virtually rotate' back the image for the image manipulation
1709 if($rotate == 90 || $rotate == 270) {
1720 /* creates new image of that size */
1721 $dst_img = imagecreatetruecolor($new_w, $new_h);
1723 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1725 /* copies resized portion of original image into new image */
1726 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1728 /* needs the image to be flipped horizontal? */
1730 $this->_debug("(FLIP)");
1731 $dst_img = $this->flipImage($dst_img, 'hori');
1733 /* needs the image to be flipped vertical? */
1735 $this->_debug("(FLIP)");
1736 $dst_img = $this->flipImage($dst_img, 'vert');
1740 $this->_debug("(ROTATE)");
1741 $dst_img = $this->rotateImage($dst_img, $rotate);
1744 /* write down new generated file */
1745 $result = imagejpeg($dst_img, $thumb_image, 75);
1747 /* free your mind */
1748 imagedestroy($dst_img);
1749 imagedestroy($src_img);
1751 if($result === false) {
1752 print "Can't write thumbnail ". $thumb_image ."\n";
1762 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1764 /* needs the image to be flipped horizontal? */
1766 $this->_debug("(FLIP)");
1767 $src_img->rotateImage(new ImagickPixel(), 90);
1768 $src_img->flipImage();
1769 $src_img->rotateImage(new ImagickPixel(), -90);
1771 /* needs the image to be flipped vertical? */
1773 $this->_debug("(FLIP)");
1774 $src_img->flipImage();
1778 $this->_debug("(ROTATE)");
1779 $src_img->rotateImage(new ImagickPixel(), $rotate);
1782 $src_img->setCompressionQuality(75);
1783 $src_img->setImageFormat('jpeg');
1785 if(!$src_img->writeImage($thumb_image)) {
1786 print "Can't write thumbnail ". $thumb_image ."\n";
1791 $src_img->destroy();
1798 } // create_thumbnail()
1801 * return all exif meta data from the file
1802 * @param string $file
1805 public function get_meta_informations($file)
1807 return exif_read_data($file);
1809 } // get_meta_informations()
1812 * create phpfspot own sqlite database
1814 * this function creates phpfspots own sqlite database
1815 * if it does not exist yet. this own is used to store
1816 * some necessary informations (md5 sum's, ...).
1818 public function check_config_table()
1820 // if the config table doesn't exist yet, create it
1821 if(!$this->cfg_db->db_check_table_exists("images")) {
1822 $this->cfg_db->db_exec("
1823 CREATE TABLE images (
1824 img_idx int primary key,
1830 } // check_config_table
1833 * Generates a thumbnail from photo idx
1835 * This function will generate JPEG thumbnails from provided F-Spot photo
1838 * 1. Check if all thumbnail generations (width) are already in place and
1840 * 2. Check if the md5sum of the original file has changed
1841 * 3. Generate the thumbnails if needed
1842 * @param integer $idx
1843 * @param integer $force
1844 * @param boolean $overwrite
1846 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1850 $resolutions = Array(
1851 $this->cfg->thumb_width,
1852 $this->cfg->photo_width,
1853 $this->cfg->mini_width,
1857 /* get details from F-Spot's database */
1858 $details = $this->get_photo_details($idx);
1860 /* calculate file MD5 sum */
1861 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1863 if(!file_exists($full_path)) {
1864 $this->_error("File ". $full_path ." does not exist\n");
1868 if(!is_readable($full_path)) {
1869 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1873 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1875 /* If Nikon NEF format, we need to treat it another way */
1876 if(isset($this->cfg->dcraw_bin) &&
1877 file_exists($this->cfg->dcraw_bin) &&
1878 is_executable($this->cfg->dcraw_bin) &&
1879 preg_match('/\.nef$/i', $details['uri'])) {
1881 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1883 /* if PPM file does not exist, let dcraw convert it from NEF */
1884 if(!file_exists($ppm_path)) {
1885 system($this->cfg->dcraw_bin ." -a ". $full_path);
1888 /* for now we handle the PPM instead of the NEF */
1889 $full_path = $ppm_path;
1893 $file_md5 = md5_file($full_path);
1896 foreach($resolutions as $resolution) {
1898 $generate_it = false;
1900 $thumb_sub_path = substr($file_md5, 0, 2);
1901 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1903 /* if thumbnail-subdirectory does not exist yet, create it */
1904 if(!file_exists(dirname($thumb_path))) {
1905 mkdir(dirname($thumb_path), 0755);
1908 /* if the thumbnail file doesn't exist, create it */
1909 if(!file_exists($thumb_path)) {
1910 $generate_it = true;
1912 /* if the file hasn't changed there is no need to regen the thumb */
1913 elseif($file_md5 != $this->getMD5($idx) || $force) {
1914 $generate_it = true;
1917 if($generate_it || $overwrite) {
1919 $this->_debug(" ". $resolution ."px");
1920 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1928 $this->_debug(" already exist");
1931 /* set the new/changed MD5 sum for the current photo */
1933 $this->setMD5($idx, $file_md5);
1936 $this->_debug("\n");
1941 * returns stored md5 sum for a specific photo
1943 * this function queries the phpfspot database for a
1944 * stored MD5 checksum of the specified photo
1945 * @param integer $idx
1946 * @return string|null
1948 public function getMD5($idx)
1950 $result = $this->cfg_db->db_query("
1953 WHERE img_idx='". $idx ."'
1959 $img = $this->cfg_db->db_fetch_object($result);
1960 return $img['img_md5'];
1965 * set MD5 sum for the specific photo
1966 * @param integer $idx
1967 * @param string $md5
1969 private function setMD5($idx, $md5)
1971 $result = $this->cfg_db->db_exec("
1972 REPLACE INTO images (img_idx, img_md5)
1973 VALUES ('". $idx ."', '". $md5 ."')
1979 * store current tag condition
1981 * this function stores the current tag condition
1982 * (AND or OR) in the users session variables
1983 * @param string $mode
1986 public function setTagCondition($mode)
1988 $_SESSION['tag_condition'] = $mode;
1992 } // setTagCondition()
1995 * invoke tag & date search
1997 * this function will return all matching tags and store
1998 * them in the session variable selected_tags. furthermore
1999 * it also handles the date search.
2000 * getPhotoSelection() will then only return the matching
2004 public function startSearch()
2007 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
2008 $from = $_POST['from'];
2010 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
2014 /* tag-name search */
2015 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2016 $searchfor_tag = $_POST['for_tag'];
2017 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2020 unset($_SESSION['searchfor_tag']);
2023 /* file-name search */
2024 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2025 $_SESSION['searchfor_name'] = $_POST['for_name'];
2028 unset($_SESSION['searchfor_name']);
2032 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2034 $_SESSION['rate_from'] = $_POST['rate_from'];
2036 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2037 $_SESSION['rate_to'] = $_POST['rate_to'];
2041 /* delete any previously set value */
2042 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2047 if(isset($from) && !empty($from))
2048 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
2050 unset($_SESSION['from_date']);
2052 if(isset($to) && !empty($to))
2053 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
2055 unset($_SESSION['to_date']);
2057 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2058 /* new search, reset the current selected tags */
2059 $_SESSION['selected_tags'] = Array();
2060 foreach($this->avail_tags as $tag) {
2061 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2062 array_push($_SESSION['selected_tags'], $tag);
2071 * updates sort order in session variable
2073 * this function is invoked by RPC and will sort the requested
2074 * sort order in the session variable.
2075 * @param string $sort_order
2078 public function updateSortOrder($order)
2080 if(isset($this->sort_orders[$order])) {
2081 $_SESSION['sort_order'] = $order;
2085 return "unkown error";
2087 } // updateSortOrder()
2092 * this function rotates the image according the
2094 * @param string $img
2095 * @param integer $degress
2098 private function rotateImage($img, $degrees)
2100 if(function_exists("imagerotate")) {
2101 $img = imagerotate($img, $degrees, 0);
2103 function imagerotate($src_img, $angle)
2105 $src_x = imagesx($src_img);
2106 $src_y = imagesy($src_img);
2107 if ($angle == 180) {
2111 elseif ($src_x <= $src_y) {
2115 elseif ($src_x >= $src_y) {
2120 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2121 imagealphablending($rotate, false);
2126 for ($y = 0; $y < ($src_y); $y++) {
2127 for ($x = 0; $x < ($src_x); $x++) {
2128 $color = imagecolorat($src_img, $x, $y);
2129 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2135 for ($y = 0; $y < ($src_y); $y++) {
2136 for ($x = 0; $x < ($src_x); $x++) {
2137 $color = imagecolorat($src_img, $x, $y);
2138 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2144 for ($y = 0; $y < ($src_y); $y++) {
2145 for ($x = 0; $x < ($src_x); $x++) {
2146 $color = imagecolorat($src_img, $x, $y);
2147 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2161 $img = imagerotate($img, $degrees);
2170 * returns flipped image
2172 * this function will return an either horizontal or
2173 * vertical flipped truecolor image.
2174 * @param string $image
2175 * @param string $mode
2178 private function flipImage($image, $mode)
2180 $w = imagesx($image);
2181 $h = imagesy($image);
2182 $flipped = imagecreatetruecolor($w, $h);
2186 for ($y = 0; $y < $h; $y++) {
2187 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2191 for ($x = 0; $x < $w; $x++) {
2192 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2202 * return all assigned tags for the specified photo
2203 * @param integer $idx
2206 private function get_photo_tags($idx)
2208 $result = $this->db->db_query("
2211 INNER JOIN photo_tags pt
2213 WHERE pt.photo_id='". $idx ."'
2218 while($row = $this->db->db_fetch_object($result)) {
2219 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2221 $tags[$row['id']] = $row['name'];
2226 } // get_photo_tags()
2229 * create on-the-fly images with text within
2230 * @param string $txt
2231 * @param string $color
2232 * @param integer $space
2233 * @param integer $font
2236 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2238 if (strlen($color) != 6)
2241 $int = hexdec($color);
2242 $h = imagefontheight($font);
2243 $fw = imagefontwidth($font);
2244 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2245 $lines = count($txt);
2246 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2247 $bg = imagecolorallocate($im, 255, 255, 255);
2248 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2251 foreach ($txt as $text) {
2252 $x = (($w - ($fw * strlen($text))) / 2);
2253 imagestring($im, $font, $x, $y, $text, $color);
2254 $y += ($h + $space);
2257 Header("Content-type: image/png");
2260 } // showTextImage()
2263 * check if all requirements are met
2266 private function check_requirements()
2268 if(!function_exists("imagecreatefromjpeg")) {
2269 print "PHP GD library extension is missing<br />\n";
2273 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2274 print "PHP SQLite3 library extension is missing<br />\n";
2278 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2279 ini_set('track_errors', 1);
2280 @include_once 'HTML/AJAX/Server.php';
2281 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2282 print "PEAR HTML_AJAX package is missing<br />\n";
2285 @include_once 'Calendar/Calendar.php';
2286 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2287 print "PEAR Calendar package is missing<br />\n";
2290 @include_once 'Console/Getopt.php';
2291 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2292 print "PEAR Console_Getopt package is missing<br />\n";
2295 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2296 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2297 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2300 ini_restore('track_errors');
2307 } // check_requirements()
2309 private function _debug($text)
2311 if(isset($this->fromcmd)) {
2318 * check if specified MIME type is supported
2319 * @param string $mime
2322 public function checkifImageSupported($mime)
2324 $supported_types = Array(
2327 "image/x-portable-pixmap",
2331 if(in_array($mime, $supported_types))
2336 } // checkifImageSupported()
2340 * @param string $text
2342 public function _error($text)
2344 switch($this->cfg->logging) {
2347 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2348 print $text ."<br />\n";
2354 error_log($text, 3, $his->cfg->log_file);
2358 $this->runtime_error = true;
2363 * output calendard input fields
2364 * @param string $mode
2367 private function get_calendar($mode)
2369 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2370 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2371 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2373 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2374 if(!isset($_SESSION[$mode .'_date']))
2375 $output.= " disabled=\"disabled\"";
2377 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2378 if(!isset($_SESSION[$mode .'_date']))
2379 $output.= " disabled=\"disabled\"";
2381 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2382 if(!isset($_SESSION[$mode .'_date']))
2383 $output.= " disabled=\"disabled\"";
2391 * output calendar matrix
2392 * @param integer $year
2393 * @param integer $month
2394 * @param integer $day
2396 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2398 if (!isset($year)) $year = date('Y');
2399 if (!isset($month)) $month = date('m');
2400 if (!isset($day)) $day = date('d');
2405 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2406 require_once CALENDAR_ROOT.'Day.php';
2409 $month = new Calendar_Month_Weekdays($year,$month);
2412 $prevStamp = $month->prevMonth(true);
2413 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2414 $nextStamp = $month->nextMonth(true);
2415 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2417 $selectedDays = array (
2418 new Calendar_Day($year,$month,$day),
2419 new Calendar_Day($year,12,25),
2422 // Build the days in the month
2423 $month->build($selectedDays);
2425 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2426 $this->tmpl->assign('prev_month', $prev);
2427 $this->tmpl->assign('next_month', $next);
2429 while ( $day = $month->fetch() ) {
2431 if(!isset($matrix[$rows]))
2432 $matrix[$rows] = Array();
2436 $dayStamp = $day->thisDay(true);
2437 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2439 // isFirst() to find start of week
2440 if ( $day->isFirst() )
2443 if ( $day->isSelected() ) {
2444 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2445 } else if ( $day->isEmpty() ) {
2446 $string.= "<td> </td>\n";
2448 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2451 // isLast() to find end of week
2452 if ( $day->isLast() )
2453 $string.= "</tr>\n";
2455 $matrix[$rows][$cols] = $string;
2465 $this->tmpl->assign('matrix', $matrix);
2466 $this->tmpl->assign('rows', $rows);
2467 $this->tmpl->show("calendar.tpl");
2469 } // get_calendar_matrix()
2472 * output export page
2473 * @param string $mode
2475 public function getExport($mode)
2477 $pictures = $this->getPhotoSelection();
2478 $current_tags = $this->getCurrentTags();
2480 foreach($pictures as $picture) {
2482 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2483 if($current_tags != "") {
2484 $orig_url.= "&tags=". $current_tags;
2486 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2487 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2490 if($this->is_user_friendly_url()) {
2491 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2494 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2500 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2501 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2505 // "[%pictureurl% %thumbnailurl%]"
2506 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2509 case 'MoinMoinList':
2510 // " * [%pictureurl% %thumbnailurl%]"
2511 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2522 public function getRSSFeed()
2524 Header("Content-type: text/xml; charset=utf-8");
2525 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2528 xmlns:media="http://search.yahoo.com/mrss/"
2529 xmlns:dc="http://purl.org/dc/elements/1.1/"
2532 <title>phpfspot</title>
2533 <description>phpfspot RSS feed</description>
2534 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2535 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2536 <generator>phpfspot</generator>
2539 $pictures = $this->getPhotoSelection();
2540 $current_tags = $this->getCurrentTags();
2542 foreach($pictures as $picture) {
2544 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2545 if($current_tags != "") {
2546 $orig_url.= "&tags=". $current_tags;
2548 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2549 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2552 $details = $this->get_photo_details($picture);
2554 if($this->is_user_friendly_url()) {
2555 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2558 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2561 $thumb_html = htmlspecialchars("
2562 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2564 ". $details['description']);
2566 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2568 /* get EXIF information if JPEG */
2569 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2570 $meta = $this->get_meta_informations($orig_path);
2573 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2577 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2578 <link><?php print htmlspecialchars($orig_url); ?></link>
2579 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2580 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2582 <?php print $thumb_html; ?>
2584 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2599 * get all selected tags
2601 * This function will return all selected tags as one string, seperated
2605 private function getCurrentTags()
2608 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2609 foreach($_SESSION['selected_tags'] as $tag)
2610 $current_tags.= $tag .",";
2611 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2613 return $current_tags;
2615 } // getCurrentTags()
2618 * return the current photo
2620 public function getCurrentPhoto()
2622 if(isset($_SESSION['current_photo'])) {
2623 print $_SESSION['current_photo'];
2625 } // getCurrentPhoto()
2628 * tells the client browser what to do
2630 * this function is getting called via AJAX by the
2631 * client browsers. it will tell them what they have
2632 * to do next. This is necessary for directly jumping
2633 * into photo index or single photo view when the are
2634 * requested with specific URLs
2637 public function whatToDo()
2639 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2641 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2642 return "showpi_tags";
2644 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2651 * return the current process-user
2654 private function getuid()
2656 if($uid = posix_getuid()) {
2657 if($user = posix_getpwuid($uid)) {
2658 return $user['name'];
2667 * returns a select-dropdown box to select photo index sort parameters
2668 * @param array $params
2669 * @param smarty $smarty
2672 public function smarty_sort_select_list($params, &$smarty)
2676 foreach($this->sort_orders as $key => $value) {
2677 $output.= "<option value=\"". $key ."\"";
2678 if($key == $_SESSION['sort_order']) {
2679 $output.= " selected=\"selected\"";
2681 $output.= ">". $value ."</option>";
2686 } // smarty_sort_select_list()
2689 * returns the currently selected sort order
2692 private function get_sort_order()
2694 switch($_SESSION['sort_order']) {
2696 return " ORDER BY p.time ASC";
2699 return " ORDER BY p.time DESC";
2702 if($this->dbver < 9) {
2703 return " ORDER BY p.name ASC";
2706 return " ORDER BY basename(p.uri) ASC";
2710 if($this->dbver < 9) {
2711 return " ORDER BY p.name DESC";
2714 return " ORDER BY basename(p.uri) DESC";
2718 return " ORDER BY t.name ASC ,p.time ASC";
2721 return " ORDER BY t.name DESC ,p.time ASC";
2724 return " ORDER BY t.name ASC, p.rating ASC";
2727 return " ORDER BY t.name DESC, p.rating DESC";
2731 } // get_sort_order()
2734 * return the next to be shown slide show image
2736 * this function returns the URL of the next image
2737 * in the slideshow sequence.
2740 public function getNextSlideShowImage()
2742 $all_photos = $this->getPhotoSelection();
2744 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2745 $_SESSION['slideshow_img'] = 0;
2747 $_SESSION['slideshow_img']++;
2749 if($this->is_user_friendly_url()) {
2750 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2753 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2755 } // getNextSlideShowImage()
2758 * return the previous to be shown slide show image
2760 * this function returns the URL of the previous image
2761 * in the slideshow sequence.
2764 public function getPrevSlideShowImage()
2766 $all_photos = $this->getPhotoSelection();
2768 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2769 $_SESSION['slideshow_img'] = 0;
2771 $_SESSION['slideshow_img']--;
2773 if($this->is_user_friendly_url()) {
2774 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2777 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2779 } // getPrevSlideShowImage()
2781 public function resetSlideShow()
2783 if(isset($_SESSION['slideshow_img']))
2784 unset($_SESSION['slideshow_img']);
2786 } // resetSlideShow()
2791 * this function will get all photos from the fspot
2792 * database and randomly return ONE entry
2794 * saddly there is yet no sqlite3 function which returns
2795 * the bulk result in array, so we have to fill up our
2799 public function get_random_photo()
2808 /* if show_tags is set, only return details for photos which
2809 are specified to be shown
2811 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2813 INNER JOIN photo_tags pt
2818 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2821 $result = $this->db->db_query($query_str);
2823 while($row = $this->db->db_fetch_object($result)) {
2824 array_push($all, $row['id']);
2827 return $all[array_rand($all)];
2829 } // get_random_photo()
2832 * get random photo tag photo
2834 * this function will get all photos tagged with the requested
2835 * tag from the fspot database and randomly return ONE entry
2837 * saddly there is yet no sqlite3 function which returns
2838 * the bulk result in array, so we have to fill up our
2842 public function get_random_tag_photo($tagidx)
2849 INNER JOIN photo_tags pt
2853 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2861 pt.tag_id LIKE '". $tagidx ."'
2864 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2867 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2871 $result = $this->db->db_query($query_str);
2873 while($row = $this->db->db_fetch_object($result)) {
2874 array_push($all, $row['id']);
2877 return $all[array_rand($all)];
2879 } // get_random_tag_photo()
2882 * validates provided date
2884 * this function validates if the provided date
2885 * contains a valid date and will return true
2887 * @param string $date_str
2890 public function isValidDate($date_str)
2892 $timestamp = strtotime($date_str);
2894 if(is_numeric($timestamp))
2902 * timestamp to string conversion
2903 * @param integer $timestamp
2906 private function ts2str($timestamp)
2908 if(!empty($timestamp) && is_numeric($timestamp))
2909 return strftime("%Y-%m-%d", $timestamp);
2914 * extract tag-names from $_GET['tags']
2915 * @param string $tags_str
2918 private function extractTags($tags_str)
2920 $not_validated = split(',', $tags_str);
2921 $validated = array();
2923 foreach($not_validated as $tag) {
2924 if(is_numeric($tag))
2925 array_push($validated, $tag);
2933 * returns the full path to a thumbnail
2934 * @param integer $width
2935 * @param integer $photo
2938 public function get_thumb_path($width, $photo)
2940 $md5 = $this->getMD5($photo);
2941 $sub_path = substr($md5, 0, 2);
2942 return $this->cfg->thumb_path
2950 } // get_thumb_path()
2953 * returns server's virtual host name
2956 private function get_server_name()
2958 return $_SERVER['SERVER_NAME'];
2959 } // get_server_name()
2962 * returns type of webprotocol which is currently used
2965 private function get_web_protocol()
2967 if(!isset($_SERVER['HTTPS']))
2971 } // get_web_protocol()
2974 * return url to this phpfspot installation
2977 private function get_phpfspot_url()
2979 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2981 } // get_phpfspot_url()
2984 * returns the number of photos which are tagged with $tag_id
2985 * @param integer $tag_id
2988 public function get_num_photos($tag_id)
2990 if($result = $this->db->db_fetchSingleRow("
2991 SELECT count(*) as number
2994 tag_id LIKE '". $tag_id ."'")) {
2996 return $result['number'];
3002 } // get_num_photos()
3005 * check file exists and is readable
3007 * returns true, if everything is ok, otherwise false
3008 * if $silent is not set, this function will output and
3010 * @param string $file
3011 * @param boolean $silent
3014 private function check_readable($file, $silent = null)
3016 if(!file_exists($file)) {
3018 print "File \"". $file ."\" does not exist.\n";
3022 if(!is_readable($file)) {
3024 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3030 } // check_readable()
3033 * check if all needed indices are present
3035 * this function checks, if some needed indices are already
3036 * present, or if not, create them on the fly. they are
3037 * necessary to speed up some queries like that one look for
3038 * all tags, when show_tags is specified in the configuration.
3040 private function checkDbIndices()
3042 $result = $this->db->db_exec("
3043 CREATE INDEX IF NOT EXISTS
3050 } // checkDbIndices()
3053 * retrive F-Spot database version
3055 * this function will return the F-Spot database version number
3056 * It is stored within the sqlite3 database in the table meta
3057 * @return string|null
3059 public function getFspotDBVersion()
3061 if($result = $this->db->db_fetchSingleRow("
3062 SELECT data as version
3065 name LIKE 'F-Spot Database Version'
3067 return $result['version'];
3071 } // getFspotDBVersion()
3074 * parse the provided URI and will returned the requested chunk
3075 * @param string $uri
3076 * @param string $mode
3079 public function parse_uri($uri, $mode)
3081 if(($components = parse_url($uri)) !== false) {
3085 return basename($components['path']);
3088 return dirname($components['path']);
3091 return $components['path'];
3101 * validate config options
3103 * this function checks if all necessary configuration options are
3104 * specified and set.
3107 private function check_config_options()
3109 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3110 $this->_error("Please set \$page_title in phpfspot_cfg");
3112 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3113 $this->_error("Please set \$base_path in phpfspot_cfg");
3115 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3116 $this->_error("Please set \$web_path in phpfspot_cfg");
3118 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3119 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3121 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3122 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3124 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3125 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3127 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3128 $this->_error("Please set \$db_access in phpfspot_cfg");
3130 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3131 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3133 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3134 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3136 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3137 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3139 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3140 $this->_error("Please set \$photo_width in phpfspot_cfg");
3142 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3143 $this->_error("Please set \$mini_width in phpfspot_cfg");
3145 if(!isset($this->cfg->thumbs_per_page))
3146 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3148 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3149 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3151 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3152 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3154 if(!isset($this->cfg->hide_tags))
3155 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3157 if(!isset($this->cfg->theme_name))
3158 $this->_error("Please set \$theme_name in phpfspot_cfg");
3160 if(!isset($this->cfg->logging))
3161 $this->_error("Please set \$logging in phpfspot_cfg");
3163 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3165 if(!isset($this->cfg->log_file))
3166 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3168 if(!is_writeable($this->cfg->log_file))
3169 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3173 /* remove trailing slash, if set */
3174 if($this->cfg->web_path == "/")
3175 $this->cfg->web_path = "";
3176 elseif(preg_match('/\/$/', $this->cfg->web_path))
3177 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3179 return $this->runtime_error;
3181 } // check_config_options()
3184 * cleanup phpfspot own database
3186 * When photos are getting delete from F-Spot, there will remain
3187 * remain some residues in phpfspot own database. This function
3188 * will try to wipe them out.
3190 public function cleanup_phpfspot_db()
3192 $to_delete = Array();
3194 $result = $this->cfg_db->db_query("
3197 ORDER BY img_idx ASC
3200 while($row = $this->cfg_db->db_fetch_object($result)) {
3201 if(!$this->db->db_fetchSingleRow("
3204 WHERE id='". $row['img_idx'] ."'")) {
3206 array_push($to_delete, $row['img_idx'], ',');
3210 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3212 $this->cfg_db->db_exec("
3214 WHERE img_idx IN (". implode($to_delete) .")
3217 } // cleanup_phpfspot_db()
3220 * return first image of the page, the $current photo
3223 * this function is used to find out the first photo of the
3224 * current page, in which the $current photo lies. this is
3225 * used to display the correct photo, when calling showPhotoIndex()
3227 * @param integer $current
3228 * @param integer $max
3231 private function getCurrentPage($current, $max)
3233 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3234 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3235 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3241 } // getCurrentPage()
3246 * this function tries to find out the correct mime-type
3247 * for the provided file.
3248 * @param string $file
3251 public function get_mime_info($file)
3253 $details = getimagesize($file);
3255 /* if getimagesize() returns empty, try at least to find out the
3258 if(empty($details) && function_exists('mime_content_type')) {
3260 // mime_content_type is marked as deprecated in the documentation,
3261 // but is it really necessary to force users to install a PECL
3263 $details['mime'] = mime_content_type($file);
3266 return $details['mime'];
3268 } // get_mime_info()
3271 * return tag-name by tag-idx
3273 * this function returns the tag-name for the requested
3274 * tag specified by tag-idx.
3275 * @param integer $idx
3278 public function get_tag_name($idx)
3280 if($result = $this->db->db_fetchSingleRow("
3284 id LIKE '". $idx ."'")) {
3286 return $result['name'];
3295 * parse user friendly url which got rewritten by the websever
3296 * @param string $request_uri
3299 private function parse_user_friendly_url($request_uri)
3301 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3303 $options = explode('/', $request_uri);
3305 switch($options[1]) {
3307 if(is_numeric($options[2])) {
3308 $this->session_cleanup();
3309 //unset($_SESSION['start_action']);
3310 //unset($_SESSION['selected_tags']);
3311 $_GET['mode'] = 'showp';
3312 return $this->showPhoto($options[2]);
3316 if(is_numeric($options[2])) {
3317 require_once "phpfspot_img.php";
3318 $img = new PHPFSPOT_IMG;
3319 if(isset($options[3]) && is_numeric($options[3]))
3320 $img->showImg($options[2], $options[3]);
3322 $img->showImg($options[2]);
3327 if(is_numeric($options[2])) {
3328 $this->session_cleanup();
3329 $_GET['tags'] = $options[2];
3330 $_SESSION['selected_tags'] = Array($options[2]);
3331 if(isset($options[3]) && is_numeric($options[3]))
3332 $_SESSION['begin_with'] = $options[3];
3333 return $this->showPhotoIndex();
3339 } // parse_user_friendly_url()
3342 * check if user-friendly-urls are enabled
3344 * this function will return true, if the config option
3345 * $user_friendly_url has been set. Otherwise false.
3348 private function is_user_friendly_url()
3350 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3355 } // is_user_friendly_url()
3360 * this function will cleanup user's session information
3362 private function session_cleanup()
3364 unset($_SESSION['begin_with']);
3365 $this->resetDateSearch();
3366 $this->resetPhotoView();
3367 $this->resetTagSearch();
3368 $this->resetNameSearch();
3369 $this->resetDateSearch();
3372 } // session_cleanup()