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(!isset($content)) {
351 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']))
352 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
354 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
357 $this->tmpl->assign('initial_content', $content);
359 $this->tmpl->show("index.tpl");
364 * get_tags - grab all tags of f-spot's database
366 * this function will get all available tags from
367 * the f-spot database and store them within two
368 * arrays within this class for later usage. in
369 * fact, if the user requests (hide_tags) it will
370 * opt-out some of them.
372 * this function is getting called once by show()
374 private function get_tags()
376 $this->avail_tags = Array();
379 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
382 DISTINCT t1.id as id, t1.name as name
385 INNER JOIN photo_tags
386 pt2 ON pt1.photo_id=pt2.photo_id
392 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
394 t1.sort_priority ASC";
396 $result = $this->db->db_query($query_str);
400 $result = $this->db->db_query("
403 ORDER BY sort_priority ASC
407 while($row = $this->db->db_fetch_object($result)) {
409 $tag_id = $row['id'];
410 $tag_name = $row['name'];
412 /* if the user has specified to ignore this tag in phpfspot's
413 configuration, ignore it here so it does not get added to
416 if(in_array($row['name'], $this->cfg->hide_tags))
419 /* if you include the following if-clause and the user has specified
420 to only show certain tags which are specified in phpfspot's
421 configuration, ignore all others so they will not be added to the
423 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
424 !in_array($row['name'], $this->cfg->show_tags))
428 $this->tags[$tag_id] = $tag_name;
429 $this->avail_tags[$count] = $tag_id;
437 * extract all photo details
439 * retrieve all available details from f-spot's
440 * database and return them as object
441 * @param integer $idx
442 * @return object|null
444 public function get_photo_details($idx)
446 if($this->dbver < 9) {
448 SELECT p.id, p.name, p.time, p.directory_path, p.description
453 /* till F-Spot version 0.4.1 */
454 if($this->dbver < 11) {
456 SELECT p.id, p.uri, p.time, p.description
462 SELECT p.id, p.uri, p.time, p.description, p.rating
468 /* if show_tags is set, only return details for photos which
469 are specified to be shown
471 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
473 INNER JOIN photo_tags pt
477 WHERE p.id='". $idx ."'
478 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
482 WHERE p.id='". $idx ."'
486 if($result = $this->db->db_query($query_str)) {
488 $row = $this->db->db_fetch_object($result);
490 if($this->dbver < 9) {
491 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
500 } // get_photo_details
503 * returns aligned photo names
505 * this function returns aligned (length) names for
506 * an specific photo. If the length of the name exceeds
507 * $limit the name will be shrinked (...)
508 * @param integer $idx
509 * @param integer $limit
510 * @return string|null
512 public function getPhotoName($idx, $limit = 0)
514 if($details = $this->get_photo_details($idx)) {
515 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
516 $name = $this->shrink_text($long_name, $limit);
526 * get photo rating level
528 * this function will return the integer-based rating
529 * level of the photo. This can only be done, if the F-Spot
530 * database is at a specific level. If rating value can not
531 * be found, zero will be returned indicating no rating value
536 public function get_photo_rating($idx)
538 if($detail = $this->get_photo_details($idx)) {
539 if(isset($detail['rating']))
540 return $detail['rating'];
545 } // get_photo_rating()
548 * get rate-search bars
550 * this function will return the rating-bars for the
554 public function get_rate_search()
558 for($i = 1; $i <= 5; $i++) {
560 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
562 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
563 $bar.= $this->cfg->web_path ."/resources/star.png";
565 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
568 onmouseover=\"show_rate('from', ". $i .");\"
569 onmouseout=\"reset_rate('from');\"
570 onclick=\"set_rate('from', ". $i .")\" />";
575 for($i = 1; $i <= 5; $i++) {
577 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
579 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
580 $bar.= $this->cfg->web_path ."/resources/star.png";
582 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
585 onmouseover=\"show_rate('to', ". $i .");\"
586 onmouseout=\"reset_rate('to');\"
587 onclick=\"set_rate('to', ". $i .");\" />";
592 } // get_rate_search()
595 * shrink text according provided limit
597 * If the length of the name exceeds $limit the
598 * text will be shortend and some content in between
599 * will be replaced with "..."
601 * @param integer $limit
604 private function shrink_text($text, $limit)
606 if($limit != 0 && strlen($text) > $limit) {
607 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
615 * translate f-spoth photo path
617 * as the full-qualified path recorded in the f-spot database
618 * is usally not the same as on the webserver, this function
619 * will replace the path with that one specified in the cfg
620 * @param string $path
623 public function translate_path($path)
625 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
630 * control HTML ouput for a single photo
632 * this function provides all the necessary information
633 * for the single photo template.
634 * @param integer photo
636 public function showPhoto($photo)
638 /* get all photos from the current photo selection */
639 $all_photos = $this->getPhotoSelection();
640 $count = count($all_photos);
642 for($i = 0; $i < $count; $i++) {
644 // $get_next will be set, when the photo which has to
645 // be displayed has been found - this means that the
646 // next available is in fact the NEXT image (for the
648 if(isset($get_next)) {
649 $next_img = $all_photos[$i];
653 /* the next photo is our NEXT photo */
654 if($all_photos[$i] == $photo) {
658 $previous_img = $all_photos[$i];
661 if($photo == $all_photos[$i]) {
666 $details = $this->get_photo_details($photo);
673 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
674 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
676 if(!file_exists($orig_path)) {
677 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
681 if(!is_readable($orig_path)) {
682 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
686 /* If the thumbnail doesn't exist yet, try to create it */
687 if(!file_exists($thumb_path)) {
688 $this->gen_thumb($photo, true);
689 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
692 /* get mime-type, height and width from the original photo */
693 $info = getimagesize($orig_path);
695 /* get EXIF information if JPEG */
696 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
697 $meta = $this->get_meta_informations($orig_path);
700 /* If EXIF data are available, use them */
701 if(isset($meta['ExifImageWidth'])) {
702 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
704 $meta_res = $info[0] ."x". $info[1];
707 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
708 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
709 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
711 $extern_link = "index.php?mode=showp&id=". $photo;
712 $current_tags = $this->getCurrentTags();
713 if($current_tags != "") {
714 $extern_link.= "&tags=". $current_tags;
716 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
717 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
720 $this->tmpl->assign('extern_link', $extern_link);
722 if(!file_exists($thumb_path)) {
723 $this->_error("Can't open file ". $thumb_path ."\n");
727 $info_thumb = getimagesize($thumb_path);
729 $this->tmpl->assign('description', $details['description']);
730 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
731 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
733 $this->tmpl->assign('width', $info_thumb[0]);
734 $this->tmpl->assign('height', $info_thumb[1]);
735 $this->tmpl->assign('ExifMadeOn', $meta_date);
736 $this->tmpl->assign('ExifMadeWith', $meta_make);
737 $this->tmpl->assign('ExifOrigResolution', $meta_res);
738 $this->tmpl->assign('ExifFileSize', $meta_size);
740 if($this->is_user_friendly_url()) {
741 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
742 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
745 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
746 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
749 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
751 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
752 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
753 $this->tmpl->assign('current_img', $photo);
755 if(isset($previous_img)) {
756 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
757 $this->tmpl->assign('prev_img', $previous_img);
760 if(isset($next_img)) {
761 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
762 $this->tmpl->assign('next_img', $next_img);
765 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
766 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
767 $this->tmpl->assign('photo_number', $i);
768 $this->tmpl->assign('photo_count', count($all_photos));
770 return $this->tmpl->fetch("single_photo.tpl");
775 * all available tags and tag cloud
777 * this function outputs all available tags (time ordered)
778 * and in addition output them as tag cloud (tags which have
779 * many photos will appears more then others)
781 public function getAvailableTags()
783 /* retrive tags from database */
788 $result = $this->db->db_query("
789 SELECT tag_id as id, count(tag_id) as quantity
799 while($row = $this->db->db_fetch_object($result)) {
800 $tags[$row['id']] = $row['quantity'];
803 // change these font sizes if you will
804 $max_size = 125; // max font size in %
805 $min_size = 75; // min font size in %
808 $max_sat = hexdec('cc');
809 $min_sat = hexdec('44');
811 // get the largest and smallest array values
812 $max_qty = max(array_values($tags));
813 $min_qty = min(array_values($tags));
815 // find the range of values
816 $spread = $max_qty - $min_qty;
817 if (0 == $spread) { // we don't want to divide by zero
821 // determine the font-size increment
822 // this is the increase per tag quantity (times used)
823 $step = ($max_size - $min_size)/($spread);
824 $step_sat = ($max_sat - $min_sat)/($spread);
826 // loop through our tag array
827 foreach ($tags as $key => $value) {
829 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
832 // calculate CSS font-size
833 // find the $value in excess of $min_qty
834 // multiply by the font-size increment ($size)
835 // and add the $min_size set above
836 $size = $min_size + (($value - $min_qty) * $step);
837 // uncomment if you want sizes in whole %:
840 $color = $min_sat + ($value - $min_qty) * $step_sat;
846 if(isset($this->tags[$key])) {
847 if($this->is_user_friendly_url())
848 $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>, ";
850 $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>, ";
854 $output = substr($output, 0, strlen($output)-2);
857 } // getAvailableTags()
860 * output all selected tags
862 * this function output all tags which have been selected
863 * by the user. the selected tags are stored in the
864 * session-variable $_SESSION['selected_tags']
867 public function getSelectedTags($type = 'link')
869 /* retrive tags from database */
874 foreach($this->avail_tags as $tag)
876 // return all selected tags
877 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
882 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
886 <div class=\"tagresulttag\">
887 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
888 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
898 $output = substr($output, 0, strlen($output)-2);
902 return "no tags selected";
905 } // getSelectedTags()
908 * add tag to users session variable
910 * this function will add the specified to users current
911 * tag selection. if a date search has been made before
912 * it will be now cleared
915 public function addTag($tag)
917 if(!isset($_SESSION['selected_tags']))
918 $_SESSION['selected_tags'] = Array();
920 if(isset($_SESSION['searchfor_tag']))
921 unset($_SESSION['searchfor_tag']);
923 // has the user requested to hide this tag, and still someone,
924 // somehow tries to add it, don't allow this.
925 if(!isset($this->cfg->hide_tags) &&
926 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
929 if(!in_array($tag, $_SESSION['selected_tags']))
930 array_push($_SESSION['selected_tags'], $tag);
937 * remove tag to users session variable
939 * this function removes the specified tag from
940 * users current tag selection
944 public function delTag($tag)
946 if(isset($_SESSION['searchfor_tag']))
947 unset($_SESSION['searchfor_tag']);
949 if(isset($_SESSION['selected_tags'])) {
950 $key = array_search($tag, $_SESSION['selected_tags']);
951 unset($_SESSION['selected_tags'][$key]);
952 sort($_SESSION['selected_tags']);
960 * reset tag selection
962 * if there is any tag selection, it will be
965 public function resetTags()
967 if(isset($_SESSION['selected_tags']))
968 unset($_SESSION['selected_tags']);
973 * returns the value for the autocomplete tag-search
976 public function get_xml_tag_list()
978 if(!isset($_GET['search']) || !is_string($_GET['search']))
979 $_GET['search'] = '';
984 /* retrive tags from database */
987 $matched_tags = Array();
989 header("Content-Type: text/xml");
991 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
992 $string.= "<results>\n";
994 foreach($this->avail_tags as $tag)
996 if(!empty($_GET['search']) &&
997 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
998 count($matched_tags) < $length) {
1000 $count = $this->get_num_photos($tag);
1003 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1006 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1012 /* if we have collected enough items, break out */
1013 if(count($matched_tags) >= $length)
1017 $string.= "</results>\n";
1021 } // get_xml_tag_list()
1025 * reset single photo
1027 * if a specific photo was requested (external link)
1028 * unset the session variable now
1030 public function resetPhotoView()
1032 if(isset($_SESSION['current_photo']))
1033 unset($_SESSION['current_photo']);
1035 } // resetPhotoView();
1040 * if any tag search has taken place, reset it now
1042 public function resetTagSearch()
1044 if(isset($_SESSION['searchfor_tag']))
1045 unset($_SESSION['searchfor_tag']);
1047 } // resetTagSearch()
1052 * if any name search has taken place, reset it now
1054 public function resetNameSearch()
1056 if(isset($_SESSION['searchfor_name']))
1057 unset($_SESSION['searchfor_name']);
1059 } // resetNameSearch()
1064 * if any date search has taken place, reset it now.
1066 public function resetDateSearch()
1068 if(isset($_SESSION['from_date']))
1069 unset($_SESSION['from_date']);
1070 if(isset($_SESSION['to_date']))
1071 unset($_SESSION['to_date']);
1073 } // resetDateSearch();
1078 * if any rate search has taken place, reset it now.
1080 public function resetRateSearch()
1082 if(isset($_SESSION['rate_from']))
1083 unset($_SESSION['rate_from']);
1084 if(isset($_SESSION['rate_to']))
1085 unset($_SESSION['rate_to']);
1087 } // resetRateSearch();
1090 * return all photo according selection
1092 * this function returns all photos based on
1093 * the tag-selection, tag- or date-search.
1094 * the tag-search also has to take care of AND
1095 * and OR conjunctions
1098 public function getPhotoSelection()
1100 $matched_photos = Array();
1101 $additional_where_cond = "";
1103 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1104 $from_date = $_SESSION['from_date'];
1105 $to_date = $_SESSION['to_date'];
1106 $additional_where_cond.= "
1107 p.time>='". $from_date ."'
1109 p.time<='". $to_date ."'
1113 if(isset($_SESSION['searchfor_name'])) {
1115 /* check for previous conditions. if so add 'AND' */
1116 if(!empty($additional_where_cond)) {
1117 $additional_where_cond.= " AND ";
1120 if($this->dbver < 9) {
1121 $additional_where_cond.= "
1123 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1125 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1130 $additional_where_cond.= "
1132 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1134 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1140 /* limit result based on rate-search */
1141 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1143 if($this->dbver > 10) {
1145 /* check for previous conditions. if so add 'AND' */
1146 if(!empty($additional_where_cond)) {
1147 $additional_where_cond.= " AND ";
1150 $additional_where_cond.= "
1151 p.rating >= ". $_SESSION['rate_from'] ."
1153 p.rating <= ". $_SESSION['rate_to'] ."
1158 if(isset($_SESSION['sort_order'])) {
1159 $order_str = $this->get_sort_order();
1162 /* return a search result */
1163 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1165 SELECT DISTINCT pt1.photo_id
1167 INNER JOIN photo_tags pt2
1168 ON pt1.photo_id=pt2.photo_id
1172 ON pt1.photo_id=p.id
1175 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1177 if(!empty($additional_where_cond))
1178 $query_str.= "AND ". $additional_where_cond ." ";
1180 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1181 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1184 if(isset($order_str))
1185 $query_str.= $order_str;
1187 $result = $this->db->db_query($query_str);
1188 while($row = $this->db->db_fetch_object($result)) {
1189 array_push($matched_photos, $row['photo_id']);
1191 return $matched_photos;
1194 /* return according the selected tags */
1195 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1197 foreach($_SESSION['selected_tags'] as $tag)
1198 $selected.= $tag .",";
1199 $selected = substr($selected, 0, strlen($selected)-1);
1201 /* photo has to match at least on of the selected tags */
1202 if($_SESSION['tag_condition'] == 'or') {
1204 SELECT DISTINCT pt1.photo_id
1206 INNER JOIN photo_tags pt2
1207 ON pt1.photo_id=pt2.photo_id
1211 ON pt1.photo_id=p.id
1212 WHERE pt1.tag_id IN (". $selected .")
1214 if(!empty($additional_where_cond))
1215 $query_str.= "AND ". $additional_where_cond ." ";
1217 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1218 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1221 if(isset($order_str))
1222 $query_str.= $order_str;
1224 /* photo has to match all selected tags */
1225 elseif($_SESSION['tag_condition'] == 'and') {
1227 if(count($_SESSION['selected_tags']) >= 32) {
1228 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1229 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1233 /* Join together a table looking like
1235 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1237 so the query can quickly return all images matching the
1238 selected tags in an AND condition
1243 SELECT DISTINCT pt1.photo_id
1247 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1254 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1256 INNER JOIN photo_tags pt". ($i+2) ."
1257 ON pt1.photo_id=pt". ($i+2) .".photo_id
1262 ON pt1.photo_id=p.id
1264 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1265 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1267 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1270 if(!empty($additional_where_cond))
1271 $query_str.= "AND ". $additional_where_cond;
1273 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1274 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1277 if(isset($order_str))
1278 $query_str.= $order_str;
1282 $result = $this->db->db_query($query_str);
1283 while($row = $this->db->db_fetch_object($result)) {
1284 array_push($matched_photos, $row['photo_id']);
1286 return $matched_photos;
1289 /* return all available photos */
1291 SELECT DISTINCT p.id
1293 LEFT JOIN photo_tags pt
1299 if(!empty($additional_where_cond))
1300 $query_str.= "WHERE ". $additional_where_cond ." ";
1302 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1303 if(!empty($additional_where_cond))
1304 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1306 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1309 if(isset($order_str))
1310 $query_str.= $order_str;
1312 $result = $this->db->db_query($query_str);
1313 while($row = $this->db->db_fetch_object($result)) {
1314 array_push($matched_photos, $row['id']);
1316 return $matched_photos;
1318 } // getPhotoSelection()
1321 * control HTML ouput for photo index
1323 * this function provides all the necessary information
1324 * for the photo index template.
1327 public function showPhotoIndex()
1329 $photos = $this->getPhotoSelection();
1330 $current_tags = $this->getCurrentTags();
1332 $count = count($photos);
1334 /* if all thumbnails should be shown on one page */
1335 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1339 /* thumbnails should be splitted up in several pages */
1340 elseif($this->cfg->thumbs_per_page > 0) {
1342 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1346 $begin_with = $_SESSION['begin_with'];
1349 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1353 $images[$thumbs] = Array();
1354 $img_height[$thumbs] = Array();
1355 $img_width[$thumbs] = Array();
1356 $img_id[$thumbs] = Array();
1357 $img_name[$thumbs] = Array();
1358 $img_fullname[$thumbs] = Array();
1359 $img_title = Array();
1360 $img_rating = Array();
1362 for($i = $begin_with; $i < $end_with; $i++) {
1364 if(isset($photos[$i])) {
1366 $images[$thumbs] = $photos[$i];
1367 $img_id[$thumbs] = $i;
1368 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1369 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1370 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1371 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1373 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1375 if(file_exists($thumb_path)) {
1376 $info = getimagesize($thumb_path);
1377 $img_width[$thumbs] = $info[0];
1378 $img_height[$thumbs] = $info[1];
1384 // +1 for for smarty's selection iteration
1387 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1388 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1390 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1391 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1392 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1395 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1396 $this->tmpl->assign('tag_result', 1);
1399 /* do we have to display the page selector ? */
1400 if($this->cfg->thumbs_per_page != 0) {
1404 /* calculate the page switchers */
1405 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1406 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1408 if($begin_with != 0)
1409 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1410 if($end_with < $count)
1411 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1413 $photo_per_page = $this->cfg->thumbs_per_page;
1414 $last_page = ceil($count / $photo_per_page);
1416 /* get the current selected page */
1417 if($begin_with == 0) {
1421 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1428 for($i = 1; $i <= $last_page; $i++) {
1430 if($current_page == $i)
1431 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1432 elseif($current_page-1 == $i || $current_page+1 == $i)
1433 $style = "style=\"font-size: 105%;\"";
1434 elseif(($current_page-5 >= $i) && ($i != 1) ||
1435 ($current_page+5 <= $i) && ($i != $last_page))
1436 $style = "style=\"font-size: 75%;\"";
1440 $start_with = ($i*$photo_per_page)-$photo_per_page;
1442 if($this->is_user_friendly_url()) {
1443 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1446 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1448 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1452 $select.= ">". $i ."</a> ";
1454 // until 9 pages we show the selector from 1-9
1455 if($last_page <= 9) {
1456 $page_select.= $select;
1459 if($i == 1 /* first page */ ||
1460 $i == $last_page /* last page */ ||
1461 $i == $current_page /* current page */ ||
1462 $i == ceil($last_page * 0.25) /* first quater */ ||
1463 $i == ceil($last_page * 0.5) /* half */ ||
1464 $i == ceil($last_page * 0.75) /* third quater */ ||
1465 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1466 (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 */ ||
1467 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1468 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1470 $page_select.= $select;
1478 $page_select.= "......... ";
1483 /* only show the page selector if we have more then one page */
1485 $this->tmpl->assign('page_selector', $page_select);
1488 $extern_link = "index.php?mode=showpi";
1489 $rss_link = "index.php?mode=rss";
1490 if($current_tags != "") {
1491 $extern_link.= "&tags=". $current_tags;
1492 $rss_link.= "&tags=". $current_tags;
1494 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1495 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1496 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1499 $export_link = "index.php?mode=export";
1500 $slideshow_link = "index.php?mode=slideshow";
1502 $this->tmpl->assign('extern_link', $extern_link);
1503 $this->tmpl->assign('slideshow_link', $slideshow_link);
1504 $this->tmpl->assign('export_link', $export_link);
1505 $this->tmpl->assign('rss_link', $rss_link);
1506 $this->tmpl->assign('count', $count);
1507 $this->tmpl->assign('width', $this->cfg->thumb_width);
1508 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1509 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1510 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1511 $this->tmpl->assign('images', $images);
1512 $this->tmpl->assign('img_width', $img_width);
1513 $this->tmpl->assign('img_height', $img_height);
1514 $this->tmpl->assign('img_id', $img_id);
1515 $this->tmpl->assign('img_name', $img_name);
1516 $this->tmpl->assign('img_fullname', $img_fullname);
1517 $this->tmpl->assign('img_title', $img_title);
1518 $this->tmpl->assign('img_rating', $img_rating);
1519 $this->tmpl->assign('thumbs', $thumbs);
1520 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1522 $result = $this->tmpl->fetch("photo_index.tpl");
1524 /* if we are returning to photo index from an photo-view,
1525 scroll the window to the last shown photo-thumbnail.
1526 after this, unset the last_photo session variable.
1528 if(isset($_SESSION['last_photo'])) {
1529 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1530 unset($_SESSION['last_photo']);
1535 } // showPhotoIndex()
1538 * show credit template
1540 public function showCredits()
1542 $this->tmpl->assign('version', $this->cfg->version);
1543 $this->tmpl->assign('product', $this->cfg->product);
1544 $this->tmpl->assign('db_version', $this->dbver);
1545 $this->tmpl->show("credits.tpl");
1550 * create thumbnails for the requested width
1552 * this function creates image thumbnails of $orig_image
1553 * stored as $thumb_image. It will check if the image is
1554 * in a supported format, if necessary rotate the image
1555 * (based on EXIF orientation meta headers) and re-sizing.
1556 * @param string $orig_image
1557 * @param string $thumb_image
1558 * @param integer $width
1561 public function create_thumbnail($orig_image, $thumb_image, $width)
1563 if(!file_exists($orig_image)) {
1567 $mime = $this->get_mime_info($orig_image);
1569 /* check if original photo is a support image type */
1570 if(!$this->checkifImageSupported($mime))
1577 $meta = $this->get_meta_informations($orig_image);
1583 if(isset($meta['Orientation'])) {
1584 switch($meta['Orientation']) {
1585 case 1: /* top, left */
1586 /* nothing to do */ break;
1587 case 2: /* top, right */
1588 $rotate = 0; $flip_hori = true; break;
1589 case 3: /* bottom, left */
1590 $rotate = 180; break;
1591 case 4: /* bottom, right */
1592 $flip_vert = true; break;
1593 case 5: /* left side, top */
1594 $rotate = 90; $flip_vert = true; break;
1595 case 6: /* right side, top */
1596 $rotate = 90; break;
1597 case 7: /* left side, bottom */
1598 $rotate = 270; $flip_vert = true; break;
1599 case 8: /* right side, bottom */
1600 $rotate = 270; break;
1604 $src_img = @imagecreatefromjpeg($orig_image);
1610 $src_img = @imagecreatefrompng($orig_image);
1614 case 'image/x-portable-pixmap':
1616 $src_img = new Imagick($orig_image);
1617 $handler = "imagick";
1622 if(!isset($src_img) || empty($src_img)) {
1623 print "Can't load image from ". $orig_image ."\n";
1631 /* grabs the height and width */
1632 $cur_width = imagesx($src_img);
1633 $cur_height = imagesy($src_img);
1635 // If requested width is more then the actual image width,
1636 // do not generate a thumbnail, instead safe the original
1637 // as thumbnail but with lower quality. But if the image
1638 // is to heigh too, then we still have to resize it.
1639 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1640 $result = imagejpeg($src_img, $thumb_image, 75);
1641 imagedestroy($src_img);
1648 $cur_width = $src_img->getImageWidth();
1649 $cur_height = $src_img->getImageHeight();
1651 // If requested width is more then the actual image width,
1652 // do not generate a thumbnail, instead safe the original
1653 // as thumbnail but with lower quality. But if the image
1654 // is to heigh too, then we still have to resize it.
1655 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1656 $src_img->setCompressionQuality(75);
1657 $src_img->setImageFormat('jpeg');
1658 $src_img->writeImage($thumb_image);
1660 $src_img->destroy();
1667 // If the image will be rotate because EXIF orientation said so
1668 // 'virtually rotate' the image for further calculations
1669 if($rotate == 90 || $rotate == 270) {
1671 $cur_width = $cur_height;
1675 /* calculates aspect ratio */
1676 $aspect_ratio = $cur_height / $cur_width;
1679 if($aspect_ratio < 1) {
1681 $new_h = abs($new_w * $aspect_ratio);
1683 /* 'virtually' rotate the image and calculate it's ratio */
1684 $tmp_w = $cur_height;
1685 $tmp_h = $cur_width;
1686 /* now get the ratio from the 'rotated' image */
1687 $tmp_ratio = $tmp_h/$tmp_w;
1688 /* now calculate the new dimensions */
1690 $tmp_h = abs($tmp_w * $tmp_ratio);
1692 // now that we know, how high they photo should be, if it
1693 // gets rotated, use this high to scale the image
1695 $new_w = abs($new_h / $aspect_ratio);
1697 // If the image will be rotate because EXIF orientation said so
1698 // now 'virtually rotate' back the image for the image manipulation
1699 if($rotate == 90 || $rotate == 270) {
1710 /* creates new image of that size */
1711 $dst_img = imagecreatetruecolor($new_w, $new_h);
1713 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1715 /* copies resized portion of original image into new image */
1716 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1718 /* needs the image to be flipped horizontal? */
1720 $this->_debug("(FLIP)");
1721 $dst_img = $this->flipImage($dst_img, 'hori');
1723 /* needs the image to be flipped vertical? */
1725 $this->_debug("(FLIP)");
1726 $dst_img = $this->flipImage($dst_img, 'vert');
1730 $this->_debug("(ROTATE)");
1731 $dst_img = $this->rotateImage($dst_img, $rotate);
1734 /* write down new generated file */
1735 $result = imagejpeg($dst_img, $thumb_image, 75);
1737 /* free your mind */
1738 imagedestroy($dst_img);
1739 imagedestroy($src_img);
1741 if($result === false) {
1742 print "Can't write thumbnail ". $thumb_image ."\n";
1752 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1754 /* needs the image to be flipped horizontal? */
1756 $this->_debug("(FLIP)");
1757 $src_img->rotateImage(new ImagickPixel(), 90);
1758 $src_img->flipImage();
1759 $src_img->rotateImage(new ImagickPixel(), -90);
1761 /* needs the image to be flipped vertical? */
1763 $this->_debug("(FLIP)");
1764 $src_img->flipImage();
1768 $this->_debug("(ROTATE)");
1769 $src_img->rotateImage(new ImagickPixel(), $rotate);
1772 $src_img->setCompressionQuality(75);
1773 $src_img->setImageFormat('jpeg');
1775 if(!$src_img->writeImage($thumb_image)) {
1776 print "Can't write thumbnail ". $thumb_image ."\n";
1781 $src_img->destroy();
1788 } // create_thumbnail()
1791 * return all exif meta data from the file
1792 * @param string $file
1795 public function get_meta_informations($file)
1797 return exif_read_data($file);
1799 } // get_meta_informations()
1802 * create phpfspot own sqlite database
1804 * this function creates phpfspots own sqlite database
1805 * if it does not exist yet. this own is used to store
1806 * some necessary informations (md5 sum's, ...).
1808 public function check_config_table()
1810 // if the config table doesn't exist yet, create it
1811 if(!$this->cfg_db->db_check_table_exists("images")) {
1812 $this->cfg_db->db_exec("
1813 CREATE TABLE images (
1814 img_idx int primary key,
1820 } // check_config_table
1823 * Generates a thumbnail from photo idx
1825 * This function will generate JPEG thumbnails from provided F-Spot photo
1828 * 1. Check if all thumbnail generations (width) are already in place and
1830 * 2. Check if the md5sum of the original file has changed
1831 * 3. Generate the thumbnails if needed
1832 * @param integer $idx
1833 * @param integer $force
1834 * @param boolean $overwrite
1836 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1840 $resolutions = Array(
1841 $this->cfg->thumb_width,
1842 $this->cfg->photo_width,
1843 $this->cfg->mini_width,
1847 /* get details from F-Spot's database */
1848 $details = $this->get_photo_details($idx);
1850 /* calculate file MD5 sum */
1851 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1853 if(!file_exists($full_path)) {
1854 $this->_error("File ". $full_path ." does not exist\n");
1858 if(!is_readable($full_path)) {
1859 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1863 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1865 /* If Nikon NEF format, we need to treat it another way */
1866 if(isset($this->cfg->dcraw_bin) &&
1867 file_exists($this->cfg->dcraw_bin) &&
1868 is_executable($this->cfg->dcraw_bin) &&
1869 preg_match('/\.nef$/i', $details['uri'])) {
1871 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1873 /* if PPM file does not exist, let dcraw convert it from NEF */
1874 if(!file_exists($ppm_path)) {
1875 system($this->cfg->dcraw_bin ." -a ". $full_path);
1878 /* for now we handle the PPM instead of the NEF */
1879 $full_path = $ppm_path;
1883 $file_md5 = md5_file($full_path);
1886 foreach($resolutions as $resolution) {
1888 $generate_it = false;
1890 $thumb_sub_path = substr($file_md5, 0, 2);
1891 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1893 /* if thumbnail-subdirectory does not exist yet, create it */
1894 if(!file_exists(dirname($thumb_path))) {
1895 mkdir(dirname($thumb_path), 0755);
1898 /* if the thumbnail file doesn't exist, create it */
1899 if(!file_exists($thumb_path)) {
1900 $generate_it = true;
1902 /* if the file hasn't changed there is no need to regen the thumb */
1903 elseif($file_md5 != $this->getMD5($idx) || $force) {
1904 $generate_it = true;
1907 if($generate_it || $overwrite) {
1909 $this->_debug(" ". $resolution ."px");
1910 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1918 $this->_debug(" already exist");
1921 /* set the new/changed MD5 sum for the current photo */
1923 $this->setMD5($idx, $file_md5);
1926 $this->_debug("\n");
1931 * returns stored md5 sum for a specific photo
1933 * this function queries the phpfspot database for a
1934 * stored MD5 checksum of the specified photo
1935 * @param integer $idx
1936 * @return string|null
1938 public function getMD5($idx)
1940 $result = $this->cfg_db->db_query("
1943 WHERE img_idx='". $idx ."'
1949 $img = $this->cfg_db->db_fetch_object($result);
1950 return $img['img_md5'];
1955 * set MD5 sum for the specific photo
1956 * @param integer $idx
1957 * @param string $md5
1959 private function setMD5($idx, $md5)
1961 $result = $this->cfg_db->db_exec("
1962 REPLACE INTO images (img_idx, img_md5)
1963 VALUES ('". $idx ."', '". $md5 ."')
1969 * store current tag condition
1971 * this function stores the current tag condition
1972 * (AND or OR) in the users session variables
1973 * @param string $mode
1976 public function setTagCondition($mode)
1978 $_SESSION['tag_condition'] = $mode;
1982 } // setTagCondition()
1985 * invoke tag & date search
1987 * this function will return all matching tags and store
1988 * them in the session variable selected_tags. furthermore
1989 * it also handles the date search.
1990 * getPhotoSelection() will then only return the matching
1994 public function startSearch()
1997 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1998 $from = $_POST['from'];
2000 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
2004 /* tag-name search */
2005 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2006 $searchfor_tag = $_POST['for_tag'];
2007 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2010 unset($_SESSION['searchfor_tag']);
2013 /* file-name search */
2014 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2015 $_SESSION['searchfor_name'] = $_POST['for_name'];
2018 unset($_SESSION['searchfor_name']);
2022 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2024 $_SESSION['rate_from'] = $_POST['rate_from'];
2026 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2027 $_SESSION['rate_to'] = $_POST['rate_to'];
2031 /* delete any previously set value */
2032 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2037 if(isset($from) && !empty($from))
2038 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
2040 unset($_SESSION['from_date']);
2042 if(isset($to) && !empty($to))
2043 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
2045 unset($_SESSION['to_date']);
2047 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2048 /* new search, reset the current selected tags */
2049 $_SESSION['selected_tags'] = Array();
2050 foreach($this->avail_tags as $tag) {
2051 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2052 array_push($_SESSION['selected_tags'], $tag);
2061 * updates sort order in session variable
2063 * this function is invoked by RPC and will sort the requested
2064 * sort order in the session variable.
2065 * @param string $sort_order
2068 public function updateSortOrder($order)
2070 if(isset($this->sort_orders[$order])) {
2071 $_SESSION['sort_order'] = $order;
2075 return "unkown error";
2077 } // updateSortOrder()
2082 * this function rotates the image according the
2084 * @param string $img
2085 * @param integer $degress
2088 private function rotateImage($img, $degrees)
2090 if(function_exists("imagerotate")) {
2091 $img = imagerotate($img, $degrees, 0);
2093 function imagerotate($src_img, $angle)
2095 $src_x = imagesx($src_img);
2096 $src_y = imagesy($src_img);
2097 if ($angle == 180) {
2101 elseif ($src_x <= $src_y) {
2105 elseif ($src_x >= $src_y) {
2110 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2111 imagealphablending($rotate, false);
2116 for ($y = 0; $y < ($src_y); $y++) {
2117 for ($x = 0; $x < ($src_x); $x++) {
2118 $color = imagecolorat($src_img, $x, $y);
2119 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2125 for ($y = 0; $y < ($src_y); $y++) {
2126 for ($x = 0; $x < ($src_x); $x++) {
2127 $color = imagecolorat($src_img, $x, $y);
2128 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2134 for ($y = 0; $y < ($src_y); $y++) {
2135 for ($x = 0; $x < ($src_x); $x++) {
2136 $color = imagecolorat($src_img, $x, $y);
2137 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2151 $img = imagerotate($img, $degrees);
2160 * returns flipped image
2162 * this function will return an either horizontal or
2163 * vertical flipped truecolor image.
2164 * @param string $image
2165 * @param string $mode
2168 private function flipImage($image, $mode)
2170 $w = imagesx($image);
2171 $h = imagesy($image);
2172 $flipped = imagecreatetruecolor($w, $h);
2176 for ($y = 0; $y < $h; $y++) {
2177 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2181 for ($x = 0; $x < $w; $x++) {
2182 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2192 * return all assigned tags for the specified photo
2193 * @param integer $idx
2196 private function get_photo_tags($idx)
2198 $result = $this->db->db_query("
2201 INNER JOIN photo_tags pt
2203 WHERE pt.photo_id='". $idx ."'
2208 while($row = $this->db->db_fetch_object($result)) {
2209 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2211 $tags[$row['id']] = $row['name'];
2216 } // get_photo_tags()
2219 * create on-the-fly images with text within
2220 * @param string $txt
2221 * @param string $color
2222 * @param integer $space
2223 * @param integer $font
2226 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2228 if (strlen($color) != 6)
2231 $int = hexdec($color);
2232 $h = imagefontheight($font);
2233 $fw = imagefontwidth($font);
2234 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2235 $lines = count($txt);
2236 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2237 $bg = imagecolorallocate($im, 255, 255, 255);
2238 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2241 foreach ($txt as $text) {
2242 $x = (($w - ($fw * strlen($text))) / 2);
2243 imagestring($im, $font, $x, $y, $text, $color);
2244 $y += ($h + $space);
2247 Header("Content-type: image/png");
2250 } // showTextImage()
2253 * check if all requirements are met
2256 private function check_requirements()
2258 if(!function_exists("imagecreatefromjpeg")) {
2259 print "PHP GD library extension is missing<br />\n";
2263 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2264 print "PHP SQLite3 library extension is missing<br />\n";
2268 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2269 ini_set('track_errors', 1);
2270 @include_once 'HTML/AJAX/Server.php';
2271 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2272 print "PEAR HTML_AJAX package is missing<br />\n";
2275 @include_once 'Calendar/Calendar.php';
2276 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2277 print "PEAR Calendar package is missing<br />\n";
2280 @include_once 'Console/Getopt.php';
2281 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2282 print "PEAR Console_Getopt package is missing<br />\n";
2285 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2286 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2287 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2290 ini_restore('track_errors');
2297 } // check_requirements()
2299 private function _debug($text)
2301 if(isset($this->fromcmd)) {
2308 * check if specified MIME type is supported
2309 * @param string $mime
2312 public function checkifImageSupported($mime)
2314 $supported_types = Array(
2317 "image/x-portable-pixmap",
2321 if(in_array($mime, $supported_types))
2326 } // checkifImageSupported()
2330 * @param string $text
2332 public function _error($text)
2334 switch($this->cfg->logging) {
2337 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2338 print $text ."<br />\n";
2344 error_log($text, 3, $his->cfg->log_file);
2348 $this->runtime_error = true;
2353 * output calendard input fields
2354 * @param string $mode
2357 private function get_calendar($mode)
2359 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2360 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2361 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2363 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2364 if(!isset($_SESSION[$mode .'_date']))
2365 $output.= " disabled=\"disabled\"";
2367 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2368 if(!isset($_SESSION[$mode .'_date']))
2369 $output.= " disabled=\"disabled\"";
2371 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2372 if(!isset($_SESSION[$mode .'_date']))
2373 $output.= " disabled=\"disabled\"";
2381 * output calendar matrix
2382 * @param integer $year
2383 * @param integer $month
2384 * @param integer $day
2386 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2388 if (!isset($year)) $year = date('Y');
2389 if (!isset($month)) $month = date('m');
2390 if (!isset($day)) $day = date('d');
2395 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2396 require_once CALENDAR_ROOT.'Day.php';
2399 $month = new Calendar_Month_Weekdays($year,$month);
2402 $prevStamp = $month->prevMonth(true);
2403 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2404 $nextStamp = $month->nextMonth(true);
2405 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2407 $selectedDays = array (
2408 new Calendar_Day($year,$month,$day),
2409 new Calendar_Day($year,12,25),
2412 // Build the days in the month
2413 $month->build($selectedDays);
2415 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2416 $this->tmpl->assign('prev_month', $prev);
2417 $this->tmpl->assign('next_month', $next);
2419 while ( $day = $month->fetch() ) {
2421 if(!isset($matrix[$rows]))
2422 $matrix[$rows] = Array();
2426 $dayStamp = $day->thisDay(true);
2427 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2429 // isFirst() to find start of week
2430 if ( $day->isFirst() )
2433 if ( $day->isSelected() ) {
2434 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2435 } else if ( $day->isEmpty() ) {
2436 $string.= "<td> </td>\n";
2438 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2441 // isLast() to find end of week
2442 if ( $day->isLast() )
2443 $string.= "</tr>\n";
2445 $matrix[$rows][$cols] = $string;
2455 $this->tmpl->assign('matrix', $matrix);
2456 $this->tmpl->assign('rows', $rows);
2457 $this->tmpl->show("calendar.tpl");
2459 } // get_calendar_matrix()
2462 * output export page
2463 * @param string $mode
2465 public function getExport($mode)
2467 $pictures = $this->getPhotoSelection();
2468 $current_tags = $this->getCurrentTags();
2470 foreach($pictures as $picture) {
2472 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2473 if($current_tags != "") {
2474 $orig_url.= "&tags=". $current_tags;
2476 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2477 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2480 if($this->is_user_friendly_url()) {
2481 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2484 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2490 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2491 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2495 // "[%pictureurl% %thumbnailurl%]"
2496 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2499 case 'MoinMoinList':
2500 // " * [%pictureurl% %thumbnailurl%]"
2501 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2512 public function getRSSFeed()
2514 Header("Content-type: text/xml; charset=utf-8");
2515 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2518 xmlns:media="http://search.yahoo.com/mrss/"
2519 xmlns:dc="http://purl.org/dc/elements/1.1/"
2522 <title>phpfspot</title>
2523 <description>phpfspot RSS feed</description>
2524 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2525 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2526 <generator>phpfspot</generator>
2529 $pictures = $this->getPhotoSelection();
2530 $current_tags = $this->getCurrentTags();
2532 foreach($pictures as $picture) {
2534 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2535 if($current_tags != "") {
2536 $orig_url.= "&tags=". $current_tags;
2538 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2539 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2542 $details = $this->get_photo_details($picture);
2544 if($this->is_user_friendly_url()) {
2545 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2548 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2551 $thumb_html = htmlspecialchars("
2552 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2554 ". $details['description']);
2556 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2558 /* get EXIF information if JPEG */
2559 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2560 $meta = $this->get_meta_informations($orig_path);
2563 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2567 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2568 <link><?php print htmlspecialchars($orig_url); ?></link>
2569 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2570 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2572 <?php print $thumb_html; ?>
2574 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2589 * get all selected tags
2591 * This function will return all selected tags as one string, seperated
2595 private function getCurrentTags()
2598 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2599 foreach($_SESSION['selected_tags'] as $tag)
2600 $current_tags.= $tag .",";
2601 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2603 return $current_tags;
2605 } // getCurrentTags()
2608 * return the current photo
2610 public function getCurrentPhoto()
2612 if(isset($_SESSION['current_photo'])) {
2613 print $_SESSION['current_photo'];
2615 } // getCurrentPhoto()
2618 * tells the client browser what to do
2620 * this function is getting called via AJAX by the
2621 * client browsers. it will tell them what they have
2622 * to do next. This is necessary for directly jumping
2623 * into photo index or single photo view when the are
2624 * requested with specific URLs
2627 public function whatToDo()
2629 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2631 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2632 return "showpi_tags";
2634 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2641 * return the current process-user
2644 private function getuid()
2646 if($uid = posix_getuid()) {
2647 if($user = posix_getpwuid($uid)) {
2648 return $user['name'];
2657 * returns a select-dropdown box to select photo index sort parameters
2658 * @param array $params
2659 * @param smarty $smarty
2662 public function smarty_sort_select_list($params, &$smarty)
2666 foreach($this->sort_orders as $key => $value) {
2667 $output.= "<option value=\"". $key ."\"";
2668 if($key == $_SESSION['sort_order']) {
2669 $output.= " selected=\"selected\"";
2671 $output.= ">". $value ."</option>";
2676 } // smarty_sort_select_list()
2679 * returns the currently selected sort order
2682 private function get_sort_order()
2684 switch($_SESSION['sort_order']) {
2686 return " ORDER BY p.time ASC";
2689 return " ORDER BY p.time DESC";
2692 if($this->dbver < 9) {
2693 return " ORDER BY p.name ASC";
2696 return " ORDER BY basename(p.uri) ASC";
2700 if($this->dbver < 9) {
2701 return " ORDER BY p.name DESC";
2704 return " ORDER BY basename(p.uri) DESC";
2708 return " ORDER BY t.name ASC ,p.time ASC";
2711 return " ORDER BY t.name DESC ,p.time ASC";
2714 return " ORDER BY t.name ASC, p.rating ASC";
2717 return " ORDER BY t.name DESC, p.rating DESC";
2721 } // get_sort_order()
2724 * return the next to be shown slide show image
2726 * this function returns the URL of the next image
2727 * in the slideshow sequence.
2730 public function getNextSlideShowImage()
2732 $all_photos = $this->getPhotoSelection();
2734 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2735 $_SESSION['slideshow_img'] = 0;
2737 $_SESSION['slideshow_img']++;
2739 if($this->is_user_friendly_url()) {
2740 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2743 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2745 } // getNextSlideShowImage()
2748 * return the previous to be shown slide show image
2750 * this function returns the URL of the previous image
2751 * in the slideshow sequence.
2754 public function getPrevSlideShowImage()
2756 $all_photos = $this->getPhotoSelection();
2758 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2759 $_SESSION['slideshow_img'] = 0;
2761 $_SESSION['slideshow_img']--;
2763 if($this->is_user_friendly_url()) {
2764 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2767 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2769 } // getPrevSlideShowImage()
2771 public function resetSlideShow()
2773 if(isset($_SESSION['slideshow_img']))
2774 unset($_SESSION['slideshow_img']);
2776 } // resetSlideShow()
2781 * this function will get all photos from the fspot
2782 * database and randomly return ONE entry
2784 * saddly there is yet no sqlite3 function which returns
2785 * the bulk result in array, so we have to fill up our
2789 public function get_random_photo()
2798 /* if show_tags is set, only return details for photos which
2799 are specified to be shown
2801 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2803 INNER JOIN photo_tags pt
2808 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2811 $result = $this->db->db_query($query_str);
2813 while($row = $this->db->db_fetch_object($result)) {
2814 array_push($all, $row['id']);
2817 return $all[array_rand($all)];
2819 } // get_random_photo()
2822 * get random photo tag photo
2824 * this function will get all photos tagged with the requested
2825 * tag from the fspot database and randomly return ONE entry
2827 * saddly there is yet no sqlite3 function which returns
2828 * the bulk result in array, so we have to fill up our
2832 public function get_random_tag_photo($tagidx)
2839 INNER JOIN photo_tags pt
2843 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2851 pt.tag_id LIKE '". $tagidx ."'
2854 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2857 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2861 $result = $this->db->db_query($query_str);
2863 while($row = $this->db->db_fetch_object($result)) {
2864 array_push($all, $row['id']);
2867 return $all[array_rand($all)];
2869 } // get_random_tag_photo()
2872 * validates provided date
2874 * this function validates if the provided date
2875 * contains a valid date and will return true
2877 * @param string $date_str
2880 public function isValidDate($date_str)
2882 $timestamp = strtotime($date_str);
2884 if(is_numeric($timestamp))
2892 * timestamp to string conversion
2893 * @param integer $timestamp
2896 private function ts2str($timestamp)
2898 if(!empty($timestamp) && is_numeric($timestamp))
2899 return strftime("%Y-%m-%d", $timestamp);
2904 * extract tag-names from $_GET['tags']
2905 * @param string $tags_str
2908 private function extractTags($tags_str)
2910 $not_validated = split(',', $tags_str);
2911 $validated = array();
2913 foreach($not_validated as $tag) {
2914 if(is_numeric($tag))
2915 array_push($validated, $tag);
2923 * returns the full path to a thumbnail
2924 * @param integer $width
2925 * @param integer $photo
2928 public function get_thumb_path($width, $photo)
2930 $md5 = $this->getMD5($photo);
2931 $sub_path = substr($md5, 0, 2);
2932 return $this->cfg->thumb_path
2940 } // get_thumb_path()
2943 * returns server's virtual host name
2946 private function get_server_name()
2948 return $_SERVER['SERVER_NAME'];
2949 } // get_server_name()
2952 * returns type of webprotocol which is currently used
2955 private function get_web_protocol()
2957 if(!isset($_SERVER['HTTPS']))
2961 } // get_web_protocol()
2964 * return url to this phpfspot installation
2967 private function get_phpfspot_url()
2969 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2971 } // get_phpfspot_url()
2974 * returns the number of photos which are tagged with $tag_id
2975 * @param integer $tag_id
2978 public function get_num_photos($tag_id)
2980 if($result = $this->db->db_fetchSingleRow("
2981 SELECT count(*) as number
2984 tag_id LIKE '". $tag_id ."'")) {
2986 return $result['number'];
2992 } // get_num_photos()
2995 * check file exists and is readable
2997 * returns true, if everything is ok, otherwise false
2998 * if $silent is not set, this function will output and
3000 * @param string $file
3001 * @param boolean $silent
3004 private function check_readable($file, $silent = null)
3006 if(!file_exists($file)) {
3008 print "File \"". $file ."\" does not exist.\n";
3012 if(!is_readable($file)) {
3014 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3020 } // check_readable()
3023 * check if all needed indices are present
3025 * this function checks, if some needed indices are already
3026 * present, or if not, create them on the fly. they are
3027 * necessary to speed up some queries like that one look for
3028 * all tags, when show_tags is specified in the configuration.
3030 private function checkDbIndices()
3032 $result = $this->db->db_exec("
3033 CREATE INDEX IF NOT EXISTS
3040 } // checkDbIndices()
3043 * retrive F-Spot database version
3045 * this function will return the F-Spot database version number
3046 * It is stored within the sqlite3 database in the table meta
3047 * @return string|null
3049 public function getFspotDBVersion()
3051 if($result = $this->db->db_fetchSingleRow("
3052 SELECT data as version
3055 name LIKE 'F-Spot Database Version'
3057 return $result['version'];
3061 } // getFspotDBVersion()
3064 * parse the provided URI and will returned the requested chunk
3065 * @param string $uri
3066 * @param string $mode
3069 public function parse_uri($uri, $mode)
3071 if(($components = parse_url($uri)) !== false) {
3075 return basename($components['path']);
3078 return dirname($components['path']);
3081 return $components['path'];
3091 * validate config options
3093 * this function checks if all necessary configuration options are
3094 * specified and set.
3097 private function check_config_options()
3099 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3100 $this->_error("Please set \$page_title in phpfspot_cfg");
3102 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3103 $this->_error("Please set \$base_path in phpfspot_cfg");
3105 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3106 $this->_error("Please set \$web_path in phpfspot_cfg");
3108 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3109 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3111 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3112 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3114 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3115 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3117 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3118 $this->_error("Please set \$db_access in phpfspot_cfg");
3120 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3121 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3123 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3124 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3126 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3127 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3129 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3130 $this->_error("Please set \$photo_width in phpfspot_cfg");
3132 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3133 $this->_error("Please set \$mini_width in phpfspot_cfg");
3135 if(!isset($this->cfg->thumbs_per_page))
3136 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3138 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3139 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3141 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3142 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3144 if(!isset($this->cfg->hide_tags))
3145 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3147 if(!isset($this->cfg->theme_name))
3148 $this->_error("Please set \$theme_name in phpfspot_cfg");
3150 if(!isset($this->cfg->logging))
3151 $this->_error("Please set \$logging in phpfspot_cfg");
3153 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3155 if(!isset($this->cfg->log_file))
3156 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3158 if(!is_writeable($this->cfg->log_file))
3159 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3163 /* remove trailing slash, if set */
3164 if($this->cfg->web_path == "/")
3165 $this->cfg->web_path = "";
3166 elseif(preg_match('/\/$/', $this->cfg->web_path))
3167 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3169 return $this->runtime_error;
3171 } // check_config_options()
3174 * cleanup phpfspot own database
3176 * When photos are getting delete from F-Spot, there will remain
3177 * remain some residues in phpfspot own database. This function
3178 * will try to wipe them out.
3180 public function cleanup_phpfspot_db()
3182 $to_delete = Array();
3184 $result = $this->cfg_db->db_query("
3187 ORDER BY img_idx ASC
3190 while($row = $this->cfg_db->db_fetch_object($result)) {
3191 if(!$this->db->db_fetchSingleRow("
3194 WHERE id='". $row['img_idx'] ."'")) {
3196 array_push($to_delete, $row['img_idx'], ',');
3200 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3202 $this->cfg_db->db_exec("
3204 WHERE img_idx IN (". implode($to_delete) .")
3207 } // cleanup_phpfspot_db()
3210 * return first image of the page, the $current photo
3213 * this function is used to find out the first photo of the
3214 * current page, in which the $current photo lies. this is
3215 * used to display the correct photo, when calling showPhotoIndex()
3217 * @param integer $current
3218 * @param integer $max
3221 private function getCurrentPage($current, $max)
3223 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3224 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3225 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3231 } // getCurrentPage()
3236 * this function tries to find out the correct mime-type
3237 * for the provided file.
3238 * @param string $file
3241 public function get_mime_info($file)
3243 $details = getimagesize($file);
3245 /* if getimagesize() returns empty, try at least to find out the
3248 if(empty($details) && function_exists('mime_content_type')) {
3250 // mime_content_type is marked as deprecated in the documentation,
3251 // but is it really necessary to force users to install a PECL
3253 $details['mime'] = mime_content_type($file);
3256 return $details['mime'];
3258 } // get_mime_info()
3261 * return tag-name by tag-idx
3263 * this function returns the tag-name for the requested
3264 * tag specified by tag-idx.
3265 * @param integer $idx
3268 public function get_tag_name($idx)
3270 if($result = $this->db->db_fetchSingleRow("
3274 id LIKE '". $idx ."'")) {
3276 return $result['name'];
3285 * parse user friendly url which got rewritten by the websever
3286 * @param string $request_uri
3289 private function parse_user_friendly_url($request_uri)
3291 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3293 $options = explode('/', $request_uri);
3295 switch($options[1]) {
3297 if(is_numeric($options[2])) {
3298 $this->session_cleanup();
3299 //unset($_SESSION['start_action']);
3300 //unset($_SESSION['selected_tags']);
3301 $_GET['mode'] = 'showp';
3302 return $this->showPhoto($options[2]);
3306 if(is_numeric($options[2])) {
3307 require_once "phpfspot_img.php";
3308 $img = new PHPFSPOT_IMG;
3309 if(isset($options[3]) && is_numeric($options[3]))
3310 $img->showImg($options[2], $options[3]);
3312 $img->showImg($options[2]);
3317 if(is_numeric($options[2])) {
3318 $this->session_cleanup();
3319 $_GET['tags'] = $options[2];
3320 $_SESSION['selected_tags'] = Array($options[2]);
3321 if(isset($options[3]) && is_numeric($options[3]))
3322 $_SESSION['begin_with'] = $options[3];
3323 return $this->showPhotoIndex();
3329 } // parse_user_friendly_url()
3332 * check if user-friendly-urls are enabled
3334 * this function will return true, if the config option
3335 * $user_friendly_url has been set. Otherwise false.
3338 private function is_user_friendly_url()
3340 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3345 } // is_user_friendly_url()
3350 * this function will cleanup user's session information
3352 private function session_cleanup()
3354 unset($_SESSION['begin_with']);
3355 $this->resetDateSearch();
3356 $this->resetPhotoView();
3357 $this->resetTagSearch();
3358 $this->resetNameSearch();
3359 $this->resetDateSearch();
3362 } // session_cleanup()