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