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->sort_orders = array_merge($this->sort_orders, array(
210 'rate_asc' => 'Rate ↑',
211 'rate_desc' => 'Rate ↓',
215 /* check if all necessary indices exist */
216 $this->checkDbIndices();
218 /* if session is not yet started, do it now */
219 if(session_id() == "")
222 if(!isset($_SESSION['tag_condition']))
223 $_SESSION['tag_condition'] = 'or';
225 if(!isset($_SESSION['sort_order']))
226 $_SESSION['sort_order'] = 'date_desc';
228 if(!isset($_SESSION['searchfor_tag']))
229 $_SESSION['searchfor_tag'] = '';
231 // if begin_with is still set but thumbs_per_page is now 0, unset it
232 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
233 unset($_SESSION['begin_with']);
235 // if user-friendly-url's are enabled, set also a flag for the template handler
236 if($this->is_user_friendly_url()) {
237 $this->tmpl->assign('user_friendly_url', 'true');
242 public function __destruct()
248 * show - generate html output
250 * this function can be called after the constructor has
251 * prepared everyhing. it will load the index.tpl smarty
252 * template. if necessary it will registere pre-selects
253 * (photo index, photo, tag search, date search) into
256 public function show()
258 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
259 $this->tmpl->assign('page_title', $this->cfg->page_title);
260 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
261 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
264 if($this->is_user_friendly_url()) {
265 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
268 if(isset($_GET['mode'])) {
270 $_SESSION['start_action'] = $_GET['mode'];
272 switch($_GET['mode']) {
274 if(isset($_GET['tags'])) {
275 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
277 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
278 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
280 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
281 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
285 if(isset($_GET['tags'])) {
286 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
287 $_SESSION['start_action'] = 'showp';
289 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
290 $_SESSION['current_photo'] = $_GET['id'];
291 $_SESSION['start_action'] = 'showp';
293 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
294 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
296 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
297 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
301 $this->tmpl->show("export.tpl");
305 $this->tmpl->show("slideshow.tpl");
309 if(isset($_GET['tags'])) {
310 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
312 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
313 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
315 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
316 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
324 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
325 $this->tmpl->assign('date_search_enabled', true);
327 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
328 $this->tmpl->assign('search_from_date', $this->get_calendar('from'));
329 $this->tmpl->assign('search_to_date', $this->get_calendar('to'));
331 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
332 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
334 if(!isset($content)) {
335 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']))
336 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
338 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
341 $this->tmpl->assign('initial_content', $content);
343 $this->tmpl->show("index.tpl");
348 * get_tags - grab all tags of f-spot's database
350 * this function will get all available tags from
351 * the f-spot database and store them within two
352 * arrays within this class for later usage. in
353 * fact, if the user requests (hide_tags) it will
354 * opt-out some of them.
356 * this function is getting called once by show()
358 private function get_tags()
360 $this->avail_tags = Array();
363 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
366 DISTINCT t1.id as id, t1.name as name
369 INNER JOIN photo_tags
370 pt2 ON pt1.photo_id=pt2.photo_id
376 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
378 t1.sort_priority ASC";
380 $result = $this->db->db_query($query_str);
384 $result = $this->db->db_query("
387 ORDER BY sort_priority ASC
391 while($row = $this->db->db_fetch_object($result)) {
393 $tag_id = $row['id'];
394 $tag_name = $row['name'];
396 /* if the user has specified to ignore this tag in phpfspot's
397 configuration, ignore it here so it does not get added to
400 if(in_array($row['name'], $this->cfg->hide_tags))
403 /* if you include the following if-clause and the user has specified
404 to only show certain tags which are specified in phpfspot's
405 configuration, ignore all others so they will not be added to the
407 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
408 !in_array($row['name'], $this->cfg->show_tags))
412 $this->tags[$tag_id] = $tag_name;
413 $this->avail_tags[$count] = $tag_id;
421 * extract all photo details
423 * retrieve all available details from f-spot's
424 * database and return them as object
425 * @param integer $idx
426 * @return object|null
428 public function get_photo_details($idx)
430 if($this->dbver < 9) {
432 SELECT p.id, p.name, p.time, p.directory_path, p.description
437 /* till F-Spot version 0.4.1 */
438 if($this->dbver < 11) {
440 SELECT p.id, p.uri, p.time, p.description
446 SELECT p.id, p.uri, p.time, p.description, p.rating
452 /* if show_tags is set, only return details for photos which
453 are specified to be shown
455 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
457 INNER JOIN photo_tags pt
461 WHERE p.id='". $idx ."'
462 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
466 WHERE p.id='". $idx ."'
470 if($result = $this->db->db_query($query_str)) {
472 $row = $this->db->db_fetch_object($result);
474 if($this->dbver < 9) {
475 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
484 } // get_photo_details
487 * returns aligned photo names
489 * this function returns aligned (length) names for
490 * an specific photo. If the length of the name exceeds
491 * $limit the name will be shrinked (...)
492 * @param integer $idx
493 * @param integer $limit
494 * @return string|null
496 public function getPhotoName($idx, $limit = 0)
498 if($details = $this->get_photo_details($idx)) {
499 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
500 $name = $this->shrink_text($long_name, $limit);
510 * get photo rating level
512 * this function will return the integer-based rating
513 * level of the photo. This can only be done, if the F-Spot
514 * database is at a specific level. If rating value can not
515 * be found, zero will be returned indicating no rating value
520 public function get_photo_rating($idx)
522 if($detail = $this->get_photo_details($idx)) {
523 if(isset($detail['rating']))
524 return $detail['rating'];
529 } // get_photo_rating()
532 * shrink text according provided limit
534 * If the length of the name exceeds $limit the
535 * text will be shortend and some content in between
536 * will be replaced with "..."
538 * @param integer $limit
541 private function shrink_text($text, $limit)
543 if($limit != 0 && strlen($text) > $limit) {
544 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
552 * translate f-spoth photo path
554 * as the full-qualified path recorded in the f-spot database
555 * is usally not the same as on the webserver, this function
556 * will replace the path with that one specified in the cfg
557 * @param string $path
560 public function translate_path($path)
562 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
567 * control HTML ouput for a single photo
569 * this function provides all the necessary information
570 * for the single photo template.
571 * @param integer photo
573 public function showPhoto($photo)
575 /* get all photos from the current photo selection */
576 $all_photos = $this->getPhotoSelection();
577 $count = count($all_photos);
579 for($i = 0; $i < $count; $i++) {
581 // $get_next will be set, when the photo which has to
582 // be displayed has been found - this means that the
583 // next available is in fact the NEXT image (for the
585 if(isset($get_next)) {
586 $next_img = $all_photos[$i];
590 /* the next photo is our NEXT photo */
591 if($all_photos[$i] == $photo) {
595 $previous_img = $all_photos[$i];
598 if($photo == $all_photos[$i]) {
603 $details = $this->get_photo_details($photo);
610 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
611 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
613 if(!file_exists($orig_path)) {
614 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
618 if(!is_readable($orig_path)) {
619 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
623 /* If the thumbnail doesn't exist yet, try to create it */
624 if(!file_exists($thumb_path)) {
625 $this->gen_thumb($photo, true);
626 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
629 /* get mime-type, height and width from the original photo */
630 $info = getimagesize($orig_path);
632 /* get EXIF information if JPEG */
633 if($info['mime'] == "image/jpeg") {
634 $meta = $this->get_meta_informations($orig_path);
637 /* If EXIF data are available, use them */
638 if(isset($meta['ExifImageWidth'])) {
639 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
641 $meta_res = $info[0] ."x". $info[1];
644 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
645 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
646 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
648 $extern_link = "index.php?mode=showp&id=". $photo;
649 $current_tags = $this->getCurrentTags();
650 if($current_tags != "") {
651 $extern_link.= "&tags=". $current_tags;
653 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
654 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
657 $this->tmpl->assign('extern_link', $extern_link);
659 if(!file_exists($thumb_path)) {
660 $this->_error("Can't open file ". $thumb_path ."\n");
664 $info_thumb = getimagesize($thumb_path);
666 $this->tmpl->assign('description', $details['description']);
667 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
668 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
670 $this->tmpl->assign('width', $info_thumb[0]);
671 $this->tmpl->assign('height', $info_thumb[1]);
672 $this->tmpl->assign('ExifMadeOn', $meta_date);
673 $this->tmpl->assign('ExifMadeWith', $meta_make);
674 $this->tmpl->assign('ExifOrigResolution', $meta_res);
675 $this->tmpl->assign('ExifFileSize', $meta_size);
677 if($this->is_user_friendly_url()) {
678 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
679 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
682 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
683 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
686 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
688 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
689 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
690 $this->tmpl->assign('current_img', $photo);
693 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
694 $this->tmpl->assign('prev_img', $previous_img);
698 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
699 $this->tmpl->assign('next_img', $next_img);
701 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
702 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
703 $this->tmpl->assign('photo_number', $i);
704 $this->tmpl->assign('photo_count', count($all_photos));
706 return $this->tmpl->fetch("single_photo.tpl");
711 * all available tags and tag cloud
713 * this function outputs all available tags (time ordered)
714 * and in addition output them as tag cloud (tags which have
715 * many photos will appears more then others)
717 public function getAvailableTags()
719 /* retrive tags from database */
724 $result = $this->db->db_query("
725 SELECT tag_id as id, count(tag_id) as quantity
735 while($row = $this->db->db_fetch_object($result)) {
736 $tags[$row['id']] = $row['quantity'];
739 // change these font sizes if you will
740 $max_size = 125; // max font size in %
741 $min_size = 75; // min font size in %
744 $max_sat = hexdec('cc');
745 $min_sat = hexdec('44');
747 // get the largest and smallest array values
748 $max_qty = max(array_values($tags));
749 $min_qty = min(array_values($tags));
751 // find the range of values
752 $spread = $max_qty - $min_qty;
753 if (0 == $spread) { // we don't want to divide by zero
757 // determine the font-size increment
758 // this is the increase per tag quantity (times used)
759 $step = ($max_size - $min_size)/($spread);
760 $step_sat = ($max_sat - $min_sat)/($spread);
762 // loop through our tag array
763 foreach ($tags as $key => $value) {
765 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
768 // calculate CSS font-size
769 // find the $value in excess of $min_qty
770 // multiply by the font-size increment ($size)
771 // and add the $min_size set above
772 $size = $min_size + (($value - $min_qty) * $step);
773 // uncomment if you want sizes in whole %:
776 $color = $min_sat + ($value - $min_qty) * $step_sat;
782 if(isset($this->tags[$key])) {
783 if($this->is_user_friendly_url())
784 $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>, ";
786 $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>, ";
790 $output = substr($output, 0, strlen($output)-2);
793 } // getAvailableTags()
796 * output all selected tags
798 * this function output all tags which have been selected
799 * by the user. the selected tags are stored in the
800 * session-variable $_SESSION['selected_tags']
803 public function getSelectedTags($type = 'link')
805 /* retrive tags from database */
810 foreach($this->avail_tags as $tag)
812 // return all selected tags
813 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
818 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
822 <div class=\"tagresulttag\">
823 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
824 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
834 $output = substr($output, 0, strlen($output)-2);
838 return "no tags selected";
841 } // getSelectedTags()
844 * add tag to users session variable
846 * this function will add the specified to users current
847 * tag selection. if a date search has been made before
848 * it will be now cleared
851 public function addTag($tag)
853 if(!isset($_SESSION['selected_tags']))
854 $_SESSION['selected_tags'] = Array();
856 if(isset($_SESSION['searchfor_tag']))
857 unset($_SESSION['searchfor_tag']);
859 // has the user requested to hide this tag, and still someone,
860 // somehow tries to add it, don't allow this.
861 if(!isset($this->cfg->hide_tags) &&
862 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
865 if(!in_array($tag, $_SESSION['selected_tags']))
866 array_push($_SESSION['selected_tags'], $tag);
873 * remove tag to users session variable
875 * this function removes the specified tag from
876 * users current tag selection
880 public function delTag($tag)
882 if(isset($_SESSION['searchfor_tag']))
883 unset($_SESSION['searchfor_tag']);
885 if(isset($_SESSION['selected_tags'])) {
886 $key = array_search($tag, $_SESSION['selected_tags']);
887 unset($_SESSION['selected_tags'][$key]);
888 sort($_SESSION['selected_tags']);
896 * reset tag selection
898 * if there is any tag selection, it will be
901 public function resetTags()
903 if(isset($_SESSION['selected_tags']))
904 unset($_SESSION['selected_tags']);
909 * returns the value for the autocomplete tag-search
912 public function get_xml_tag_list()
914 if(!isset($_GET['search']) || !is_string($_GET['search']))
915 $_GET['search'] = '';
920 /* retrive tags from database */
923 $matched_tags = Array();
925 header("Content-Type: text/xml");
927 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
928 $string.= "<results>\n";
930 foreach($this->avail_tags as $tag)
932 if(!empty($_GET['search']) &&
933 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
934 count($matched_tags) < $length) {
936 $count = $this->get_num_photos($tag);
939 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
942 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
948 /* if we have collected enough items, break out */
949 if(count($matched_tags) >= $length)
953 $string.= "</results>\n";
957 } // get_xml_tag_list()
963 * if a specific photo was requested (external link)
964 * unset the session variable now
966 public function resetPhotoView()
968 if(isset($_SESSION['current_photo']))
969 unset($_SESSION['current_photo']);
971 } // resetPhotoView();
976 * if any tag search has taken place, reset it now
978 public function resetTagSearch()
980 if(isset($_SESSION['searchfor_tag']))
981 unset($_SESSION['searchfor_tag']);
983 } // resetTagSearch()
988 * if any name search has taken place, reset it now
990 public function resetNameSearch()
992 if(isset($_SESSION['searchfor_name']))
993 unset($_SESSION['searchfor_name']);
995 } // resetNameSearch()
1000 * if any date search has taken place, reset
1003 public function resetDateSearch()
1005 if(isset($_SESSION['from_date']))
1006 unset($_SESSION['from_date']);
1007 if(isset($_SESSION['to_date']))
1008 unset($_SESSION['to_date']);
1010 } // resetDateSearch();
1013 * return all photo according selection
1015 * this function returns all photos based on
1016 * the tag-selection, tag- or date-search.
1017 * the tag-search also has to take care of AND
1018 * and OR conjunctions
1021 public function getPhotoSelection()
1023 $matched_photos = Array();
1024 $additional_where_cond = "";
1026 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1027 $from_date = $_SESSION['from_date'];
1028 $to_date = $_SESSION['to_date'];
1029 $additional_where_cond.= "
1030 p.time>='". $from_date ."'
1032 p.time<='". $to_date ."'
1036 if(isset($_SESSION['searchfor_name'])) {
1037 if($this->dbver < 9) {
1038 $additional_where_cond.= "
1040 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1042 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1047 $additional_where_cond.= "
1049 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1051 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1057 if(isset($_SESSION['sort_order'])) {
1058 $order_str = $this->get_sort_order();
1061 /* return a search result */
1062 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1064 SELECT DISTINCT pt1.photo_id
1066 INNER JOIN photo_tags pt2
1067 ON pt1.photo_id=pt2.photo_id
1071 ON pt1.photo_id=p.id
1074 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1076 if(isset($additional_where_cond) && !empty($additional_where_cond))
1077 $query_str.= "AND ". $additional_where_cond ." ";
1079 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1080 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1083 if(isset($order_str))
1084 $query_str.= $order_str;
1086 $result = $this->db->db_query($query_str);
1087 while($row = $this->db->db_fetch_object($result)) {
1088 array_push($matched_photos, $row['photo_id']);
1090 return $matched_photos;
1093 /* return according the selected tags */
1094 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1096 foreach($_SESSION['selected_tags'] as $tag)
1097 $selected.= $tag .",";
1098 $selected = substr($selected, 0, strlen($selected)-1);
1100 /* photo has to match at least on of the selected tags */
1101 if($_SESSION['tag_condition'] == 'or') {
1103 SELECT DISTINCT pt1.photo_id
1105 INNER JOIN photo_tags pt2
1106 ON pt1.photo_id=pt2.photo_id
1110 ON pt1.photo_id=p.id
1111 WHERE pt1.tag_id IN (". $selected .")
1113 if(isset($additional_where_cond) && !empty($additional_where_cond))
1114 $query_str.= "AND ". $additional_where_cond ." ";
1116 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1117 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1120 if(isset($order_str))
1121 $query_str.= $order_str;
1123 /* photo has to match all selected tags */
1124 elseif($_SESSION['tag_condition'] == 'and') {
1126 if(count($_SESSION['selected_tags']) >= 32) {
1127 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1128 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1132 /* Join together a table looking like
1134 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1136 so the query can quickly return all images matching the
1137 selected tags in an AND condition
1142 SELECT DISTINCT pt1.photo_id
1146 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1153 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1155 INNER JOIN photo_tags pt". ($i+2) ."
1156 ON pt1.photo_id=pt". ($i+2) .".photo_id
1161 ON pt1.photo_id=p.id
1163 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1164 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1166 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1169 if(isset($additional_where_cond) && !empty($additional_where_cond))
1170 $query_str.= "AND ". $additional_where_cond;
1172 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1173 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1176 if(isset($order_str))
1177 $query_str.= $order_str;
1181 $result = $this->db->db_query($query_str);
1182 while($row = $this->db->db_fetch_object($result)) {
1183 array_push($matched_photos, $row['photo_id']);
1185 return $matched_photos;
1188 /* return all available photos */
1190 SELECT DISTINCT p.id
1192 LEFT JOIN photo_tags pt
1198 if(isset($additional_where_cond) && !empty($additional_where_cond))
1199 $query_str.= "WHERE ". $additional_where_cond ." ";
1201 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1202 if(isset($additional_where_cond) && !empty($additional_where_cond))
1203 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1205 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1208 if(isset($order_str))
1209 $query_str.= $order_str;
1211 $result = $this->db->db_query($query_str);
1212 while($row = $this->db->db_fetch_object($result)) {
1213 array_push($matched_photos, $row['id']);
1215 return $matched_photos;
1217 } // getPhotoSelection()
1220 * control HTML ouput for photo index
1222 * this function provides all the necessary information
1223 * for the photo index template.
1226 public function showPhotoIndex()
1228 $photos = $this->getPhotoSelection();
1230 $count = count($photos);
1232 /* if all thumbnails should be shown on one page */
1233 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1237 /* thumbnails should be splitted up in several pages */
1238 elseif($this->cfg->thumbs_per_page > 0) {
1240 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1244 $begin_with = $_SESSION['begin_with'];
1247 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1251 $images[$thumbs] = Array();
1252 $img_height[$thumbs] = Array();
1253 $img_width[$thumbs] = Array();
1254 $img_id[$thumbs] = Array();
1255 $img_name[$thumbs] = Array();
1256 $img_fullname[$thumbs] = Array();
1257 $img_title = Array();
1258 $img_rating = Array();
1260 for($i = $begin_with; $i < $end_with; $i++) {
1262 if(isset($photos[$i])) {
1264 $images[$thumbs] = $photos[$i];
1265 $img_id[$thumbs] = $i;
1266 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1267 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1268 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1269 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1271 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1273 if(file_exists($thumb_path)) {
1274 $info = getimagesize($thumb_path);
1275 $img_width[$thumbs] = $info[0];
1276 $img_height[$thumbs] = $info[1];
1282 // +1 for for smarty's selection iteration
1285 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1286 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1288 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1289 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1290 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1293 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1294 $this->tmpl->assign('tag_result', 1);
1297 /* do we have to display the page selector ? */
1298 if($this->cfg->thumbs_per_page != 0) {
1302 /* calculate the page switchers */
1303 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1304 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1306 if($begin_with != 0)
1307 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1308 if($end_with < $count)
1309 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1311 $photo_per_page = $this->cfg->thumbs_per_page;
1312 $last_page = ceil($count / $photo_per_page);
1314 /* get the current selected page */
1315 if($begin_with == 0) {
1319 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1326 for($i = 1; $i <= $last_page; $i++) {
1328 if($current_page == $i)
1329 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1330 elseif($current_page-1 == $i || $current_page+1 == $i)
1331 $style = "style=\"font-size: 105%;\"";
1332 elseif(($current_page-5 >= $i) && ($i != 1) ||
1333 ($current_page+5 <= $i) && ($i != $last_page))
1334 $style = "style=\"font-size: 75%;\"";
1338 $start_with = ($i*$photo_per_page)-$photo_per_page;
1340 if($this->is_user_friendly_url()) {
1341 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1344 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1346 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1350 $select.= ">". $i ."</a> ";
1352 // until 9 pages we show the selector from 1-9
1353 if($last_page <= 9) {
1354 $page_select.= $select;
1357 if($i == 1 /* first page */ ||
1358 $i == $last_page /* last page */ ||
1359 $i == $current_page /* current page */ ||
1360 $i == ceil($last_page * 0.25) /* first quater */ ||
1361 $i == ceil($last_page * 0.5) /* half */ ||
1362 $i == ceil($last_page * 0.75) /* third quater */ ||
1363 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1364 (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 */ ||
1365 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1366 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1368 $page_select.= $select;
1376 $page_select.= "......... ";
1381 /* only show the page selector if we have more then one page */
1383 $this->tmpl->assign('page_selector', $page_select);
1386 $current_tags = $this->getCurrentTags();
1387 $extern_link = "index.php?mode=showpi";
1388 $rss_link = "index.php?mode=rss";
1389 if($current_tags != "") {
1390 $extern_link.= "&tags=". $current_tags;
1391 $rss_link.= "&tags=". $current_tags;
1393 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1394 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1395 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1398 $export_link = "index.php?mode=export";
1399 $slideshow_link = "index.php?mode=slideshow";
1401 $this->tmpl->assign('extern_link', $extern_link);
1402 $this->tmpl->assign('slideshow_link', $slideshow_link);
1403 $this->tmpl->assign('export_link', $export_link);
1404 $this->tmpl->assign('rss_link', $rss_link);
1405 $this->tmpl->assign('count', $count);
1406 $this->tmpl->assign('width', $this->cfg->thumb_width);
1407 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1408 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1409 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1410 $this->tmpl->assign('images', $images);
1411 $this->tmpl->assign('img_width', $img_width);
1412 $this->tmpl->assign('img_height', $img_height);
1413 $this->tmpl->assign('img_id', $img_id);
1414 $this->tmpl->assign('img_name', $img_name);
1415 $this->tmpl->assign('img_fullname', $img_fullname);
1416 $this->tmpl->assign('img_title', $img_title);
1417 $this->tmpl->assign('img_rating', $img_rating);
1418 $this->tmpl->assign('thumbs', $thumbs);
1419 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1421 $result = $this->tmpl->fetch("photo_index.tpl");
1423 /* if we are returning to photo index from an photo-view,
1424 scroll the window to the last shown photo-thumbnail.
1425 after this, unset the last_photo session variable.
1427 if(isset($_SESSION['last_photo'])) {
1428 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1429 unset($_SESSION['last_photo']);
1434 } // showPhotoIndex()
1437 * show credit template
1439 public function showCredits()
1441 $this->tmpl->assign('version', $this->cfg->version);
1442 $this->tmpl->assign('product', $this->cfg->product);
1443 $this->tmpl->assign('db_version', $this->dbver);
1444 $this->tmpl->show("credits.tpl");
1449 * create thumbnails for the requested width
1451 * this function creates image thumbnails of $orig_image
1452 * stored as $thumb_image. It will check if the image is
1453 * in a supported format, if necessary rotate the image
1454 * (based on EXIF orientation meta headers) and re-sizing.
1455 * @param string $orig_image
1456 * @param string $thumb_image
1457 * @param integer $width
1460 public function create_thumbnail($orig_image, $thumb_image, $width)
1462 if(!file_exists($orig_image)) {
1466 $mime = $this->get_mime_info($orig_image);
1468 /* check if original photo is a support image type */
1469 if(!$this->checkifImageSupported($mime))
1476 $meta = $this->get_meta_informations($orig_image);
1482 switch($meta['Orientation']) {
1483 case 1: /* top, left */
1484 /* nothing to do */ break;
1485 case 2: /* top, right */
1486 $rotate = 0; $flip_hori = true; break;
1487 case 3: /* bottom, left */
1488 $rotate = 180; break;
1489 case 4: /* bottom, right */
1490 $flip_vert = true; break;
1491 case 5: /* left side, top */
1492 $rotate = 90; $flip_vert = true; break;
1493 case 6: /* right side, top */
1494 $rotate = 90; break;
1495 case 7: /* left side, bottom */
1496 $rotate = 270; $flip_vert = true; break;
1497 case 8: /* right side, bottom */
1498 $rotate = 270; break;
1501 $src_img = @imagecreatefromjpeg($orig_image);
1507 $src_img = @imagecreatefrompng($orig_image);
1511 case 'image/x-portable-pixmap':
1513 $src_img = new Imagick($orig_image);
1514 $handler = "imagick";
1519 if(!isset($src_img) || empty($src_img)) {
1520 print "Can't load image from ". $orig_image ."\n";
1528 /* grabs the height and width */
1529 $cur_width = imagesx($src_img);
1530 $cur_height = imagesy($src_img);
1532 // If requested width is more then the actual image width,
1533 // do not generate a thumbnail, instead safe the original
1534 // as thumbnail but with lower quality. But if the image
1535 // is to heigh too, then we still have to resize it.
1536 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1537 $result = imagejpeg($src_img, $thumb_image, 75);
1538 imagedestroy($src_img);
1545 $cur_width = $src_img->getImageWidth();
1546 $cur_height = $src_img->getImageHeight();
1548 // If requested width is more then the actual image width,
1549 // do not generate a thumbnail, instead safe the original
1550 // as thumbnail but with lower quality. But if the image
1551 // is to heigh too, then we still have to resize it.
1552 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1553 $src_img->setCompressionQuality(75);
1554 $src_img->setImageFormat('jpeg');
1555 $src_img->writeImage($thumb_image);
1557 $src_img->destroy();
1564 // If the image will be rotate because EXIF orientation said so
1565 // 'virtually rotate' the image for further calculations
1566 if($rotate == 90 || $rotate == 270) {
1568 $cur_width = $cur_height;
1572 /* calculates aspect ratio */
1573 $aspect_ratio = $cur_height / $cur_width;
1576 if($aspect_ratio < 1) {
1578 $new_h = abs($new_w * $aspect_ratio);
1580 /* 'virtually' rotate the image and calculate it's ratio */
1581 $tmp_w = $cur_height;
1582 $tmp_h = $cur_width;
1583 /* now get the ratio from the 'rotated' image */
1584 $tmp_ratio = $tmp_h/$tmp_w;
1585 /* now calculate the new dimensions */
1587 $tmp_h = abs($tmp_w * $tmp_ratio);
1589 // now that we know, how high they photo should be, if it
1590 // gets rotated, use this high to scale the image
1592 $new_w = abs($new_h / $aspect_ratio);
1594 // If the image will be rotate because EXIF orientation said so
1595 // now 'virtually rotate' back the image for the image manipulation
1596 if($rotate == 90 || $rotate == 270) {
1607 /* creates new image of that size */
1608 $dst_img = imagecreatetruecolor($new_w, $new_h);
1610 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1612 /* copies resized portion of original image into new image */
1613 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1615 /* needs the image to be flipped horizontal? */
1617 $this->_debug("(FLIP)");
1618 $dst_img = $this->flipImage($dst_img, 'hori');
1620 /* needs the image to be flipped vertical? */
1622 $this->_debug("(FLIP)");
1623 $dst_img = $this->flipImage($dst_img, 'vert');
1627 $this->_debug("(ROTATE)");
1628 $dst_img = $this->rotateImage($dst_img, $rotate);
1631 /* write down new generated file */
1632 $result = imagejpeg($dst_img, $thumb_image, 75);
1634 /* free your mind */
1635 imagedestroy($dst_img);
1636 imagedestroy($src_img);
1638 if($result === false) {
1639 print "Can't write thumbnail ". $thumb_image ."\n";
1649 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1651 /* needs the image to be flipped horizontal? */
1653 $this->_debug("(FLIP)");
1654 $src_img->rotateImage(new ImagickPixel(), 90);
1655 $src_img->flipImage();
1656 $src_img->rotateImage(new ImagickPixel(), -90);
1658 /* needs the image to be flipped vertical? */
1660 $this->_debug("(FLIP)");
1661 $src_img->flipImage();
1665 $this->_debug("(ROTATE)");
1666 $src_img->rotateImage(new ImagickPixel(), $rotate);
1669 $src_img->setCompressionQuality(75);
1670 $src_img->setImageFormat('jpeg');
1672 if(!$src_img->writeImage($thumb_image)) {
1673 print "Can't write thumbnail ". $thumb_image ."\n";
1678 $src_img->destroy();
1685 } // create_thumbnail()
1688 * return all exif meta data from the file
1689 * @param string $file
1692 public function get_meta_informations($file)
1694 return exif_read_data($file);
1696 } // get_meta_informations()
1699 * create phpfspot own sqlite database
1701 * this function creates phpfspots own sqlite database
1702 * if it does not exist yet. this own is used to store
1703 * some necessary informations (md5 sum's, ...).
1705 public function check_config_table()
1707 // if the config table doesn't exist yet, create it
1708 if(!$this->cfg_db->db_check_table_exists("images")) {
1709 $this->cfg_db->db_exec("
1710 CREATE TABLE images (
1711 img_idx int primary key,
1717 } // check_config_table
1720 * Generates a thumbnail from photo idx
1722 * This function will generate JPEG thumbnails from provided F-Spot photo
1725 * 1. Check if all thumbnail generations (width) are already in place and
1727 * 2. Check if the md5sum of the original file has changed
1728 * 3. Generate the thumbnails if needed
1729 * @param integer $idx
1730 * @param integer $force
1731 * @param boolean $overwrite
1733 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1737 $resolutions = Array(
1738 $this->cfg->thumb_width,
1739 $this->cfg->photo_width,
1740 $this->cfg->mini_width,
1744 /* get details from F-Spot's database */
1745 $details = $this->get_photo_details($idx);
1747 /* calculate file MD5 sum */
1748 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1750 if(!file_exists($full_path)) {
1751 $this->_error("File ". $full_path ." does not exist\n");
1755 if(!is_readable($full_path)) {
1756 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1760 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1762 /* If Nikon NEF format, we need to treat it another way */
1763 if(isset($this->cfg->dcraw_bin) &&
1764 file_exists($this->cfg->dcraw_bin) &&
1765 is_executable($this->cfg->dcraw_bin) &&
1766 preg_match('/\.nef$/i', $details['uri'])) {
1768 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1770 /* if PPM file does not exist, let dcraw convert it from NEF */
1771 if(!file_exists($ppm_path)) {
1772 system($this->cfg->dcraw_bin ." -a ". $full_path);
1775 /* for now we handle the PPM instead of the NEF */
1776 $full_path = $ppm_path;
1780 $file_md5 = md5_file($full_path);
1783 foreach($resolutions as $resolution) {
1785 $generate_it = false;
1787 $thumb_sub_path = substr($file_md5, 0, 2);
1788 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1790 /* if thumbnail-subdirectory does not exist yet, create it */
1791 if(!file_exists(dirname($thumb_path))) {
1792 mkdir(dirname($thumb_path), 0755);
1795 /* if the thumbnail file doesn't exist, create it */
1796 if(!file_exists($thumb_path)) {
1797 $generate_it = true;
1799 /* if the file hasn't changed there is no need to regen the thumb */
1800 elseif($file_md5 != $this->getMD5($idx) || $force) {
1801 $generate_it = true;
1804 if($generate_it || $overwrite) {
1806 $this->_debug(" ". $resolution ."px");
1807 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1815 $this->_debug(" already exist");
1818 /* set the new/changed MD5 sum for the current photo */
1820 $this->setMD5($idx, $file_md5);
1823 $this->_debug("\n");
1828 * returns stored md5 sum for a specific photo
1830 * this function queries the phpfspot database for a
1831 * stored MD5 checksum of the specified photo
1832 * @param integer $idx
1833 * @return string|null
1835 public function getMD5($idx)
1837 $result = $this->cfg_db->db_query("
1840 WHERE img_idx='". $idx ."'
1846 $img = $this->cfg_db->db_fetch_object($result);
1847 return $img['img_md5'];
1852 * set MD5 sum for the specific photo
1853 * @param integer $idx
1854 * @param string $md5
1856 private function setMD5($idx, $md5)
1858 $result = $this->cfg_db->db_exec("
1859 REPLACE INTO images (img_idx, img_md5)
1860 VALUES ('". $idx ."', '". $md5 ."')
1866 * store current tag condition
1868 * this function stores the current tag condition
1869 * (AND or OR) in the users session variables
1870 * @param string $mode
1873 public function setTagCondition($mode)
1875 $_SESSION['tag_condition'] = $mode;
1879 } // setTagCondition()
1882 * invoke tag & date search
1884 * this function will return all matching tags and store
1885 * them in the session variable selected_tags. furthermore
1886 * it also handles the date search.
1887 * getPhotoSelection() will then only return the matching
1891 public function startSearch()
1893 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1894 $from = $_POST['from'];
1896 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1900 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1901 $searchfor_tag = $_POST['for_tag'];
1902 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1905 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1906 $searchfor_name = $_POST['for_name'];
1907 $_SESSION['searchfor_name'] = $_POST['for_name'];
1912 if(isset($from) && !empty($from))
1913 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1915 unset($_SESSION['from_date']);
1917 if(isset($to) && !empty($to))
1918 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1920 unset($_SESSION['to_date']);
1922 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1923 /* new search, reset the current selected tags */
1924 $_SESSION['selected_tags'] = Array();
1925 foreach($this->avail_tags as $tag) {
1926 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1927 array_push($_SESSION['selected_tags'], $tag);
1936 * updates sort order in session variable
1938 * this function is invoked by RPC and will sort the requested
1939 * sort order in the session variable.
1940 * @param string $sort_order
1943 public function updateSortOrder($order)
1945 if(isset($this->sort_orders[$order])) {
1946 $_SESSION['sort_order'] = $order;
1950 return "unkown error";
1952 } // updateSortOrder()
1957 * this function rotates the image according the
1959 * @param string $img
1960 * @param integer $degress
1963 private function rotateImage($img, $degrees)
1965 if(function_exists("imagerotate")) {
1966 $img = imagerotate($img, $degrees, 0);
1968 function imagerotate($src_img, $angle)
1970 $src_x = imagesx($src_img);
1971 $src_y = imagesy($src_img);
1972 if ($angle == 180) {
1976 elseif ($src_x <= $src_y) {
1980 elseif ($src_x >= $src_y) {
1985 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1986 imagealphablending($rotate, false);
1991 for ($y = 0; $y < ($src_y); $y++) {
1992 for ($x = 0; $x < ($src_x); $x++) {
1993 $color = imagecolorat($src_img, $x, $y);
1994 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2000 for ($y = 0; $y < ($src_y); $y++) {
2001 for ($x = 0; $x < ($src_x); $x++) {
2002 $color = imagecolorat($src_img, $x, $y);
2003 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2009 for ($y = 0; $y < ($src_y); $y++) {
2010 for ($x = 0; $x < ($src_x); $x++) {
2011 $color = imagecolorat($src_img, $x, $y);
2012 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2026 $img = imagerotate($img, $degrees);
2035 * returns flipped image
2037 * this function will return an either horizontal or
2038 * vertical flipped truecolor image.
2039 * @param string $image
2040 * @param string $mode
2043 private function flipImage($image, $mode)
2045 $w = imagesx($image);
2046 $h = imagesy($image);
2047 $flipped = imagecreatetruecolor($w, $h);
2051 for ($y = 0; $y < $h; $y++) {
2052 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2056 for ($x = 0; $x < $w; $x++) {
2057 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2067 * return all assigned tags for the specified photo
2068 * @param integer $idx
2071 private function get_photo_tags($idx)
2073 $result = $this->db->db_query("
2076 INNER JOIN photo_tags pt
2078 WHERE pt.photo_id='". $idx ."'
2083 while($row = $this->db->db_fetch_object($result)) {
2084 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2086 $tags[$row['id']] = $row['name'];
2091 } // get_photo_tags()
2094 * create on-the-fly images with text within
2095 * @param string $txt
2096 * @param string $color
2097 * @param integer $space
2098 * @param integer $font
2101 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2103 if (strlen($color) != 6)
2106 $int = hexdec($color);
2107 $h = imagefontheight($font);
2108 $fw = imagefontwidth($font);
2109 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2110 $lines = count($txt);
2111 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2112 $bg = imagecolorallocate($im, 255, 255, 255);
2113 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2116 foreach ($txt as $text) {
2117 $x = (($w - ($fw * strlen($text))) / 2);
2118 imagestring($im, $font, $x, $y, $text, $color);
2119 $y += ($h + $space);
2122 Header("Content-type: image/png");
2125 } // showTextImage()
2128 * check if all requirements are met
2131 private function check_requirements()
2133 if(!function_exists("imagecreatefromjpeg")) {
2134 print "PHP GD library extension is missing<br />\n";
2138 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2139 print "PHP SQLite3 library extension is missing<br />\n";
2143 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2144 ini_set('track_errors', 1);
2145 @include_once 'HTML/AJAX/Server.php';
2146 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2147 print "PEAR HTML_AJAX package is missing<br />\n";
2150 @include_once 'Calendar/Calendar.php';
2151 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2152 print "PEAR Calendar package is missing<br />\n";
2155 @include_once 'Console/Getopt.php';
2156 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2157 print "PEAR Console_Getopt package is missing<br />\n";
2160 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2161 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2162 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2165 ini_restore('track_errors');
2172 } // check_requirements()
2174 private function _debug($text)
2176 if($this->fromcmd) {
2183 * check if specified MIME type is supported
2184 * @param string $mime
2187 public function checkifImageSupported($mime)
2189 $supported_types = Array(
2192 "image/x-portable-pixmap",
2196 if(in_array($mime, $supported_types))
2201 } // checkifImageSupported()
2205 * @param string $text
2207 public function _error($text)
2209 switch($this->cfg->logging) {
2212 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2213 print $text ."<br />\n";
2219 error_log($text, 3, $his->cfg->log_file);
2223 $this->runtime_error = true;
2228 * output calendard input fields
2229 * @param string $mode
2232 private function get_calendar($mode)
2234 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2235 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2236 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2238 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2239 if(!isset($_SESSION[$mode .'_date']))
2240 $output.= " disabled=\"disabled\"";
2242 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2243 if(!isset($_SESSION[$mode .'_date']))
2244 $output.= " disabled=\"disabled\"";
2246 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2247 if(!isset($_SESSION[$mode .'_date']))
2248 $output.= " disabled=\"disabled\"";
2256 * output calendar matrix
2257 * @param integer $year
2258 * @param integer $month
2259 * @param integer $day
2261 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2263 if (!isset($year)) $year = date('Y');
2264 if (!isset($month)) $month = date('m');
2265 if (!isset($day)) $day = date('d');
2270 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2271 require_once CALENDAR_ROOT.'Day.php';
2274 $month = new Calendar_Month_Weekdays($year,$month);
2277 $prevStamp = $month->prevMonth(true);
2278 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2279 $nextStamp = $month->nextMonth(true);
2280 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2282 $selectedDays = array (
2283 new Calendar_Day($year,$month,$day),
2284 new Calendar_Day($year,12,25),
2287 // Build the days in the month
2288 $month->build($selectedDays);
2290 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2291 $this->tmpl->assign('prev_month', $prev);
2292 $this->tmpl->assign('next_month', $next);
2294 while ( $day = $month->fetch() ) {
2296 if(!isset($matrix[$rows]))
2297 $matrix[$rows] = Array();
2301 $dayStamp = $day->thisDay(true);
2302 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2304 // isFirst() to find start of week
2305 if ( $day->isFirst() )
2308 if ( $day->isSelected() ) {
2309 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2310 } else if ( $day->isEmpty() ) {
2311 $string.= "<td> </td>\n";
2313 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2316 // isLast() to find end of week
2317 if ( $day->isLast() )
2318 $string.= "</tr>\n";
2320 $matrix[$rows][$cols] = $string;
2330 $this->tmpl->assign('matrix', $matrix);
2331 $this->tmpl->assign('rows', $rows);
2332 $this->tmpl->show("calendar.tpl");
2334 } // get_calendar_matrix()
2337 * output export page
2338 * @param string $mode
2340 public function getExport($mode)
2342 $pictures = $this->getPhotoSelection();
2343 $current_tags = $this->getCurrentTags();
2345 foreach($pictures as $picture) {
2347 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2348 if($current_tags != "") {
2349 $orig_url.= "&tags=". $current_tags;
2351 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2352 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2355 if($this->is_user_friendly_url()) {
2356 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2359 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2365 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2366 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2370 // "[%pictureurl% %thumbnailurl%]"
2371 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2374 case 'MoinMoinList':
2375 // " * [%pictureurl% %thumbnailurl%]"
2376 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2387 public function getRSSFeed()
2389 Header("Content-type: text/xml; charset=utf-8");
2390 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2393 xmlns:media="http://search.yahoo.com/mrss/"
2394 xmlns:dc="http://purl.org/dc/elements/1.1/"
2397 <title>phpfspot</title>
2398 <description>phpfspot RSS feed</description>
2399 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2400 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2401 <generator>phpfspot</generator>
2404 $pictures = $this->getPhotoSelection();
2405 $current_tags = $this->getCurrentTags();
2407 foreach($pictures as $picture) {
2409 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2410 if($current_tags != "") {
2411 $orig_url.= "&tags=". $current_tags;
2413 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2414 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2417 $details = $this->get_photo_details($picture);
2419 if($this->is_user_friendly_url()) {
2420 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2423 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2426 $thumb_html = htmlspecialchars("
2427 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2429 ". $details['description']);
2431 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2433 /* get EXIF information if JPEG */
2434 if($details['mime'] == "image/jpeg") {
2435 $meta = $this->get_meta_informations($orig_path);
2438 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2442 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2443 <link><?php print htmlspecialchars($orig_url); ?></link>
2444 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2445 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2447 <?php print $thumb_html; ?>
2449 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2464 * return all selected tags as one string
2467 private function getCurrentTags()
2470 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2471 foreach($_SESSION['selected_tags'] as $tag)
2472 $current_tags.= $tag .",";
2473 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2475 return $current_tags;
2477 } // getCurrentTags()
2480 * return the current photo
2482 public function getCurrentPhoto()
2484 if(isset($_SESSION['current_photo'])) {
2485 print $_SESSION['current_photo'];
2487 } // getCurrentPhoto()
2490 * tells the client browser what to do
2492 * this function is getting called via AJAX by the
2493 * client browsers. it will tell them what they have
2494 * to do next. This is necessary for directly jumping
2495 * into photo index or single photo view when the are
2496 * requested with specific URLs
2499 public function whatToDo()
2501 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2503 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2504 return "showpi_tags";
2506 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2513 * return the current process-user
2516 private function getuid()
2518 if($uid = posix_getuid()) {
2519 if($user = posix_getpwuid($uid)) {
2520 return $user['name'];
2529 * returns a select-dropdown box to select photo index sort parameters
2530 * @param array $params
2531 * @param smarty $smarty
2534 public function smarty_sort_select_list($params, &$smarty)
2538 foreach($this->sort_orders as $key => $value) {
2539 $output.= "<option value=\"". $key ."\"";
2540 if($key == $_SESSION['sort_order']) {
2541 $output.= " selected=\"selected\"";
2543 $output.= ">". $value ."</option>";
2548 } // smarty_sort_select_list()
2551 * returns the currently selected sort order
2554 private function get_sort_order()
2556 switch($_SESSION['sort_order']) {
2558 return " ORDER BY p.time ASC";
2561 return " ORDER BY p.time DESC";
2564 if($this->dbver < 9) {
2565 return " ORDER BY p.name ASC";
2568 return " ORDER BY basename(p.uri) ASC";
2572 if($this->dbver < 9) {
2573 return " ORDER BY p.name DESC";
2576 return " ORDER BY basename(p.uri) DESC";
2580 return " ORDER BY t.name ASC ,p.time ASC";
2583 return " ORDER BY t.name DESC ,p.time ASC";
2586 return " ORDER BY t.name ASC, p.rating ASC";
2589 return " ORDER BY t.name DESC, p.rating DESC";
2593 } // get_sort_order()
2596 * return the next to be shown slide show image
2598 * this function returns the URL of the next image
2599 * in the slideshow sequence.
2602 public function getNextSlideShowImage()
2604 $all_photos = $this->getPhotoSelection();
2606 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2607 $_SESSION['slideshow_img'] = 0;
2609 $_SESSION['slideshow_img']++;
2611 if($this->is_user_friendly_url()) {
2612 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2615 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2617 } // getNextSlideShowImage()
2620 * return the previous to be shown slide show image
2622 * this function returns the URL of the previous image
2623 * in the slideshow sequence.
2626 public function getPrevSlideShowImage()
2628 $all_photos = $this->getPhotoSelection();
2630 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2631 $_SESSION['slideshow_img'] = 0;
2633 $_SESSION['slideshow_img']--;
2635 if($this->is_user_friendly_url()) {
2636 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2639 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2641 } // getPrevSlideShowImage()
2643 public function resetSlideShow()
2645 if(isset($_SESSION['slideshow_img']))
2646 unset($_SESSION['slideshow_img']);
2648 } // resetSlideShow()
2653 * this function will get all photos from the fspot
2654 * database and randomly return ONE entry
2656 * saddly there is yet no sqlite3 function which returns
2657 * the bulk result in array, so we have to fill up our
2661 public function get_random_photo()
2670 /* if show_tags is set, only return details for photos which
2671 are specified to be shown
2673 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2675 INNER JOIN photo_tags pt
2680 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2683 $result = $this->db->db_query($query_str);
2685 while($row = $this->db->db_fetch_object($result)) {
2686 array_push($all, $row['id']);
2689 return $all[array_rand($all)];
2691 } // get_random_photo()
2694 * get random photo tag photo
2696 * this function will get all photos tagged with the requested
2697 * tag from the fspot database and randomly return ONE entry
2699 * saddly there is yet no sqlite3 function which returns
2700 * the bulk result in array, so we have to fill up our
2704 public function get_random_tag_photo($tagidx)
2711 INNER JOIN photo_tags pt
2715 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2723 pt.tag_id LIKE '". $tagidx ."'
2726 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2729 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2733 $result = $this->db->db_query($query_str);
2735 while($row = $this->db->db_fetch_object($result)) {
2736 array_push($all, $row['id']);
2739 return $all[array_rand($all)];
2741 } // get_random_tag_photo()
2744 * validates provided date
2746 * this function validates if the provided date
2747 * contains a valid date and will return true
2749 * @param string $date_str
2752 public function isValidDate($date_str)
2754 $timestamp = strtotime($date_str);
2756 if(is_numeric($timestamp))
2764 * timestamp to string conversion
2765 * @param integer $timestamp
2768 private function ts2str($timestamp)
2770 if(!empty($timestamp) && is_numeric($timestamp))
2771 return strftime("%Y-%m-%d", $timestamp);
2776 * extract tag-names from $_GET['tags']
2777 * @param string $tags_str
2780 private function extractTags($tags_str)
2782 $not_validated = split(',', $tags_str);
2783 $validated = array();
2785 foreach($not_validated as $tag) {
2786 if(is_numeric($tag))
2787 array_push($validated, $tag);
2795 * returns the full path to a thumbnail
2796 * @param integer $width
2797 * @param integer $photo
2800 public function get_thumb_path($width, $photo)
2802 $md5 = $this->getMD5($photo);
2803 $sub_path = substr($md5, 0, 2);
2804 return $this->cfg->thumb_path
2812 } // get_thumb_path()
2815 * returns server's virtual host name
2818 private function get_server_name()
2820 return $_SERVER['SERVER_NAME'];
2821 } // get_server_name()
2824 * returns type of webprotocol which is currently used
2827 private function get_web_protocol()
2829 if(!isset($_SERVER['HTTPS']))
2833 } // get_web_protocol()
2836 * return url to this phpfspot installation
2839 private function get_phpfspot_url()
2841 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2843 } // get_phpfspot_url()
2846 * returns the number of photos which are tagged with $tag_id
2847 * @param integer $tag_id
2850 public function get_num_photos($tag_id)
2852 if($result = $this->db->db_fetchSingleRow("
2853 SELECT count(*) as number
2856 tag_id LIKE '". $tag_id ."'")) {
2858 return $result['number'];
2864 } // get_num_photos()
2867 * check file exists and is readable
2869 * returns true, if everything is ok, otherwise false
2870 * if $silent is not set, this function will output and
2872 * @param string $file
2873 * @param boolean $silent
2876 private function check_readable($file, $silent = null)
2878 if(!file_exists($file)) {
2880 print "File \"". $file ."\" does not exist.\n";
2884 if(!is_readable($file)) {
2886 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2892 } // check_readable()
2895 * check if all needed indices are present
2897 * this function checks, if some needed indices are already
2898 * present, or if not, create them on the fly. they are
2899 * necessary to speed up some queries like that one look for
2900 * all tags, when show_tags is specified in the configuration.
2902 private function checkDbIndices()
2904 $result = $this->db->db_exec("
2905 CREATE INDEX IF NOT EXISTS
2912 } // checkDbIndices()
2915 * retrive F-Spot database version
2917 * this function will return the F-Spot database version number
2918 * It is stored within the sqlite3 database in the table meta
2919 * @return string|null
2921 public function getFspotDBVersion()
2923 if($result = $this->db->db_fetchSingleRow("
2924 SELECT data as version
2927 name LIKE 'F-Spot Database Version'
2929 return $result['version'];
2933 } // getFspotDBVersion()
2936 * parse the provided URI and will returned the requested chunk
2937 * @param string $uri
2938 * @param string $mode
2941 public function parse_uri($uri, $mode)
2943 if(($components = parse_url($uri)) !== false) {
2947 return basename($components['path']);
2950 return dirname($components['path']);
2953 return $components['path'];
2963 * validate config options
2965 * this function checks if all necessary configuration options are
2966 * specified and set.
2969 private function check_config_options()
2971 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2972 $this->_error("Please set \$page_title in phpfspot_cfg");
2974 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2975 $this->_error("Please set \$base_path in phpfspot_cfg");
2977 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2978 $this->_error("Please set \$web_path in phpfspot_cfg");
2980 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2981 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2983 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2984 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2986 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2987 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2989 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2990 $this->_error("Please set \$db_access in phpfspot_cfg");
2992 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2993 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2995 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2996 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2998 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2999 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3001 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3002 $this->_error("Please set \$photo_width in phpfspot_cfg");
3004 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3005 $this->_error("Please set \$mini_width in phpfspot_cfg");
3007 if(!isset($this->cfg->thumbs_per_page))
3008 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3010 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3011 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3013 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3014 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3016 if(!isset($this->cfg->hide_tags))
3017 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3019 if(!isset($this->cfg->theme_name))
3020 $this->_error("Please set \$theme_name in phpfspot_cfg");
3022 if(!isset($this->cfg->logging))
3023 $this->_error("Please set \$logging in phpfspot_cfg");
3025 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3027 if(!isset($this->cfg->log_file))
3028 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3030 if(!is_writeable($this->cfg->log_file))
3031 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3035 /* remove trailing slash, if set */
3036 if($this->cfg->web_path == "/")
3037 $this->cfg->web_path = "";
3038 elseif(preg_match('/\/$/', $this->cfg->web_path))
3039 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3041 return $this->runtime_error;
3043 } // check_config_options()
3046 * cleanup phpfspot own database
3048 * When photos are getting delete from F-Spot, there will remain
3049 * remain some residues in phpfspot own database. This function
3050 * will try to wipe them out.
3052 public function cleanup_phpfspot_db()
3054 $to_delete = Array();
3056 $result = $this->cfg_db->db_query("
3059 ORDER BY img_idx ASC
3062 while($row = $this->cfg_db->db_fetch_object($result)) {
3063 if(!$this->db->db_fetchSingleRow("
3066 WHERE id='". $row['img_idx'] ."'")) {
3068 array_push($to_delete, $row['img_idx'], ',');
3072 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3074 $this->cfg_db->db_exec("
3076 WHERE img_idx IN (". implode($to_delete) .")
3079 } // cleanup_phpfspot_db()
3082 * return first image of the page, the $current photo
3085 * this function is used to find out the first photo of the
3086 * current page, in which the $current photo lies. this is
3087 * used to display the correct photo, when calling showPhotoIndex()
3089 * @param integer $current
3090 * @param integer $max
3093 private function getCurrentPage($current, $max)
3095 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3096 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3097 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3103 } // getCurrentPage()
3108 * this function tries to find out the correct mime-type
3109 * for the provided file.
3110 * @param string $file
3113 public function get_mime_info($file)
3115 $details = getimagesize($file);
3117 /* if getimagesize() returns empty, try at least to find out the
3120 if(empty($details) && function_exists('mime_content_type')) {
3122 // mime_content_type is marked as deprecated in the documentation,
3123 // but is it really necessary to force users to install a PECL
3125 $details['mime'] = mime_content_type($file);
3128 return $details['mime'];
3130 } // get_mime_info()
3133 * return tag-name by tag-idx
3135 * this function returns the tag-name for the requested
3136 * tag specified by tag-idx.
3137 * @param integer $idx
3140 public function get_tag_name($idx)
3142 if($result = $this->db->db_fetchSingleRow("
3146 id LIKE '". $idx ."'")) {
3148 return $result['name'];
3157 * parse user friendly url which got rewritten by the websever
3158 * @param string $request_uri
3161 private function parse_user_friendly_url($request_uri)
3163 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3165 $options = explode('/', $request_uri);
3167 switch($options[1]) {
3169 if(is_numeric($options[2])) {
3170 $this->session_cleanup();
3171 //unset($_SESSION['start_action']);
3172 //unset($_SESSION['selected_tags']);
3173 $_GET['mode'] = 'showp';
3174 return $this->showPhoto($options[2]);
3178 if(is_numeric($options[2])) {
3179 require_once "phpfspot_img.php";
3180 $img = new PHPFSPOT_IMG;
3181 if(isset($options[3]) && is_numeric($options[3]))
3182 $img->showImg($options[2], $options[3]);
3184 $img->showImg($options[2]);
3189 if(is_numeric($options[2])) {
3190 $this->session_cleanup();
3191 $_GET['tags'] = $options[2];
3192 $_SESSION['selected_tags'] = Array($options[2]);
3193 if(isset($options[3]) && is_numeric($options[3]))
3194 $_SESSION['begin_with'] = $options[3];
3195 return $this->showPhotoIndex();
3201 } // parse_user_friendly_url()
3204 * check if user-friendly-urls are enabled
3206 * this function will return true, if the config option
3207 * $user_friendly_url has been set. Otherwise false.
3210 private function is_user_friendly_url()
3212 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3217 } // is_user_friendly_url()
3222 * this function will cleanup user's session information
3224 private function session_cleanup()
3226 unset($_SESSION['begin_with']);
3227 $this->resetDateSearch();
3228 $this->resetPhotoView();
3229 $this->resetTagSearch();
3230 $this->resetNameSearch();
3231 $this->resetDateSearch();
3234 } // session_cleanup()