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 /* fetch slideshow template */
307 print $this->tmpl->show("slideshow.tpl");
308 /* no further execution necessary. */
312 if(isset($_GET['tags'])) {
313 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
315 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
316 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
318 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
319 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
327 /* if date-search variables are registered in the session, set the check
328 for "consider date-range" in the html output
330 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
331 $this->tmpl->assign('date_search_enabled', true);
333 /* if rate-search variables are registered in the session, set the check
334 for "consider rate-range" in the html output
336 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
337 $this->tmpl->assign('rate_search_enabled', true);
340 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
341 $this->tmpl->assign('search_from_date', $this->get_calendar('from'));
342 $this->tmpl->assign('search_to_date', $this->get_calendar('to'));
344 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
345 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
346 $this->tmpl->assign('rate_search', $this->get_rate_search());
348 if(!isset($content)) {
349 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']))
350 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
352 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
355 $this->tmpl->assign('initial_content', $content);
357 $this->tmpl->show("index.tpl");
362 * get_tags - grab all tags of f-spot's database
364 * this function will get all available tags from
365 * the f-spot database and store them within two
366 * arrays within this class for later usage. in
367 * fact, if the user requests (hide_tags) it will
368 * opt-out some of them.
370 * this function is getting called once by show()
372 private function get_tags()
374 $this->avail_tags = Array();
377 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
380 DISTINCT t1.id as id, t1.name as name
383 INNER JOIN photo_tags
384 pt2 ON pt1.photo_id=pt2.photo_id
390 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
392 t1.sort_priority ASC";
394 $result = $this->db->db_query($query_str);
398 $result = $this->db->db_query("
401 ORDER BY sort_priority ASC
405 while($row = $this->db->db_fetch_object($result)) {
407 $tag_id = $row['id'];
408 $tag_name = $row['name'];
410 /* if the user has specified to ignore this tag in phpfspot's
411 configuration, ignore it here so it does not get added to
414 if(in_array($row['name'], $this->cfg->hide_tags))
417 /* if you include the following if-clause and the user has specified
418 to only show certain tags which are specified in phpfspot's
419 configuration, ignore all others so they will not be added to the
421 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
422 !in_array($row['name'], $this->cfg->show_tags))
426 $this->tags[$tag_id] = $tag_name;
427 $this->avail_tags[$count] = $tag_id;
435 * extract all photo details
437 * retrieve all available details from f-spot's
438 * database and return them as object
439 * @param integer $idx
440 * @return object|null
442 public function get_photo_details($idx)
444 if($this->dbver < 9) {
446 SELECT p.id, p.name, p.time, p.directory_path, p.description
451 /* till F-Spot version 0.4.1 */
452 if($this->dbver < 11) {
454 SELECT p.id, p.uri, p.time, p.description
460 SELECT p.id, p.uri, p.time, p.description, p.rating
466 /* if show_tags is set, only return details for photos which
467 are specified to be shown
469 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
471 INNER JOIN photo_tags pt
475 WHERE p.id='". $idx ."'
476 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
480 WHERE p.id='". $idx ."'
484 if($result = $this->db->db_query($query_str)) {
486 $row = $this->db->db_fetch_object($result);
488 if($this->dbver < 9) {
489 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
498 } // get_photo_details
501 * returns aligned photo names
503 * this function returns aligned (length) names for
504 * an specific photo. If the length of the name exceeds
505 * $limit the name will be shrinked (...)
506 * @param integer $idx
507 * @param integer $limit
508 * @return string|null
510 public function getPhotoName($idx, $limit = 0)
512 if($details = $this->get_photo_details($idx)) {
513 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
514 $name = $this->shrink_text($long_name, $limit);
524 * get photo rating level
526 * this function will return the integer-based rating
527 * level of the photo. This can only be done, if the F-Spot
528 * database is at a specific level. If rating value can not
529 * be found, zero will be returned indicating no rating value
534 public function get_photo_rating($idx)
536 if($detail = $this->get_photo_details($idx)) {
537 if(isset($detail['rating']))
538 return $detail['rating'];
543 } // get_photo_rating()
546 * get rate-search bars
548 * this function will return the rating-bars for the
552 public function get_rate_search()
556 for($i = 1; $i <= 5; $i++) {
558 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
560 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
561 $bar.= $this->cfg->web_path ."/resources/star.png";
563 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
566 onmouseover=\"show_rate('from', ". $i .");\"
567 onmouseout=\"reset_rate('from');\"
568 onclick=\"set_rate('from', ". $i .")\" />";
573 for($i = 1; $i <= 5; $i++) {
575 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
577 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
578 $bar.= $this->cfg->web_path ."/resources/star.png";
580 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
583 onmouseover=\"show_rate('to', ". $i .");\"
584 onmouseout=\"reset_rate('to');\"
585 onclick=\"set_rate('to', ". $i .");\" />";
590 } // get_rate_search()
593 * shrink text according provided limit
595 * If the length of the name exceeds $limit the
596 * text will be shortend and some content in between
597 * will be replaced with "..."
599 * @param integer $limit
602 private function shrink_text($text, $limit)
604 if($limit != 0 && strlen($text) > $limit) {
605 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
613 * translate f-spoth photo path
615 * as the full-qualified path recorded in the f-spot database
616 * is usally not the same as on the webserver, this function
617 * will replace the path with that one specified in the cfg
618 * @param string $path
621 public function translate_path($path)
623 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
628 * control HTML ouput for a single photo
630 * this function provides all the necessary information
631 * for the single photo template.
632 * @param integer photo
634 public function showPhoto($photo)
636 /* get all photos from the current photo selection */
637 $all_photos = $this->getPhotoSelection();
638 $count = count($all_photos);
640 for($i = 0; $i < $count; $i++) {
642 // $get_next will be set, when the photo which has to
643 // be displayed has been found - this means that the
644 // next available is in fact the NEXT image (for the
646 if(isset($get_next)) {
647 $next_img = $all_photos[$i];
651 /* the next photo is our NEXT photo */
652 if($all_photos[$i] == $photo) {
656 $previous_img = $all_photos[$i];
659 if($photo == $all_photos[$i]) {
664 $details = $this->get_photo_details($photo);
671 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
672 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
674 if(!file_exists($orig_path)) {
675 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
679 if(!is_readable($orig_path)) {
680 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
684 /* If the thumbnail doesn't exist yet, try to create it */
685 if(!file_exists($thumb_path)) {
686 $this->gen_thumb($photo, true);
687 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
690 /* get mime-type, height and width from the original photo */
691 $info = getimagesize($orig_path);
693 /* get EXIF information if JPEG */
694 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
695 $meta = $this->get_meta_informations($orig_path);
698 /* If EXIF data are available, use them */
699 if(isset($meta['ExifImageWidth'])) {
700 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
702 $meta_res = $info[0] ."x". $info[1];
705 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
706 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
707 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
709 $extern_link = "index.php?mode=showp&id=". $photo;
710 $current_tags = $this->getCurrentTags();
711 if($current_tags != "") {
712 $extern_link.= "&tags=". $current_tags;
714 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
715 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
718 $this->tmpl->assign('extern_link', $extern_link);
720 if(!file_exists($thumb_path)) {
721 $this->_error("Can't open file ". $thumb_path ."\n");
725 $info_thumb = getimagesize($thumb_path);
727 $this->tmpl->assign('description', $details['description']);
728 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
729 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
731 $this->tmpl->assign('width', $info_thumb[0]);
732 $this->tmpl->assign('height', $info_thumb[1]);
733 $this->tmpl->assign('ExifMadeOn', $meta_date);
734 $this->tmpl->assign('ExifMadeWith', $meta_make);
735 $this->tmpl->assign('ExifOrigResolution', $meta_res);
736 $this->tmpl->assign('ExifFileSize', $meta_size);
738 if($this->is_user_friendly_url()) {
739 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
740 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
743 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
744 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
747 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
749 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
750 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
751 $this->tmpl->assign('current_img', $photo);
753 if(isset($previous_img)) {
754 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
755 $this->tmpl->assign('prev_img', $previous_img);
758 if(isset($next_img)) {
759 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
760 $this->tmpl->assign('next_img', $next_img);
763 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
764 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
765 $this->tmpl->assign('photo_number', $i);
766 $this->tmpl->assign('photo_count', count($all_photos));
768 return $this->tmpl->fetch("single_photo.tpl");
773 * all available tags and tag cloud
775 * this function outputs all available tags (time ordered)
776 * and in addition output them as tag cloud (tags which have
777 * many photos will appears more then others)
779 public function getAvailableTags()
781 /* retrive tags from database */
786 $result = $this->db->db_query("
787 SELECT tag_id as id, count(tag_id) as quantity
797 while($row = $this->db->db_fetch_object($result)) {
798 $tags[$row['id']] = $row['quantity'];
801 // change these font sizes if you will
802 $max_size = 125; // max font size in %
803 $min_size = 75; // min font size in %
806 $max_sat = hexdec('cc');
807 $min_sat = hexdec('44');
809 // get the largest and smallest array values
810 $max_qty = max(array_values($tags));
811 $min_qty = min(array_values($tags));
813 // find the range of values
814 $spread = $max_qty - $min_qty;
815 if (0 == $spread) { // we don't want to divide by zero
819 // determine the font-size increment
820 // this is the increase per tag quantity (times used)
821 $step = ($max_size - $min_size)/($spread);
822 $step_sat = ($max_sat - $min_sat)/($spread);
824 // loop through our tag array
825 foreach ($tags as $key => $value) {
827 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
830 // calculate CSS font-size
831 // find the $value in excess of $min_qty
832 // multiply by the font-size increment ($size)
833 // and add the $min_size set above
834 $size = $min_size + (($value - $min_qty) * $step);
835 // uncomment if you want sizes in whole %:
838 $color = $min_sat + ($value - $min_qty) * $step_sat;
844 if(isset($this->tags[$key])) {
845 if($this->is_user_friendly_url())
846 $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>, ";
848 $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>, ";
852 $output = substr($output, 0, strlen($output)-2);
855 } // getAvailableTags()
858 * output all selected tags
860 * this function output all tags which have been selected
861 * by the user. the selected tags are stored in the
862 * session-variable $_SESSION['selected_tags']
865 public function getSelectedTags($type = 'link')
867 /* retrive tags from database */
872 foreach($this->avail_tags as $tag)
874 // return all selected tags
875 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
880 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
884 <div class=\"tagresulttag\">
885 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
886 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
896 $output = substr($output, 0, strlen($output)-2);
900 return "no tags selected";
903 } // getSelectedTags()
906 * add tag to users session variable
908 * this function will add the specified to users current
909 * tag selection. if a date search has been made before
910 * it will be now cleared
913 public function addTag($tag)
915 if(!isset($_SESSION['selected_tags']))
916 $_SESSION['selected_tags'] = Array();
918 if(isset($_SESSION['searchfor_tag']))
919 unset($_SESSION['searchfor_tag']);
921 // has the user requested to hide this tag, and still someone,
922 // somehow tries to add it, don't allow this.
923 if(!isset($this->cfg->hide_tags) &&
924 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
927 if(!in_array($tag, $_SESSION['selected_tags']))
928 array_push($_SESSION['selected_tags'], $tag);
935 * remove tag to users session variable
937 * this function removes the specified tag from
938 * users current tag selection
942 public function delTag($tag)
944 if(isset($_SESSION['searchfor_tag']))
945 unset($_SESSION['searchfor_tag']);
947 if(isset($_SESSION['selected_tags'])) {
948 $key = array_search($tag, $_SESSION['selected_tags']);
949 unset($_SESSION['selected_tags'][$key]);
950 sort($_SESSION['selected_tags']);
958 * reset tag selection
960 * if there is any tag selection, it will be
963 public function resetTags()
965 if(isset($_SESSION['selected_tags']))
966 unset($_SESSION['selected_tags']);
971 * returns the value for the autocomplete tag-search
974 public function get_xml_tag_list()
976 if(!isset($_GET['search']) || !is_string($_GET['search']))
977 $_GET['search'] = '';
982 /* retrive tags from database */
985 $matched_tags = Array();
987 header("Content-Type: text/xml");
989 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
990 $string.= "<results>\n";
992 foreach($this->avail_tags as $tag)
994 if(!empty($_GET['search']) &&
995 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
996 count($matched_tags) < $length) {
998 $count = $this->get_num_photos($tag);
1001 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1004 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1010 /* if we have collected enough items, break out */
1011 if(count($matched_tags) >= $length)
1015 $string.= "</results>\n";
1019 } // get_xml_tag_list()
1023 * reset single photo
1025 * if a specific photo was requested (external link)
1026 * unset the session variable now
1028 public function resetPhotoView()
1030 if(isset($_SESSION['current_photo']))
1031 unset($_SESSION['current_photo']);
1033 } // resetPhotoView();
1038 * if any tag search has taken place, reset it now
1040 public function resetTagSearch()
1042 if(isset($_SESSION['searchfor_tag']))
1043 unset($_SESSION['searchfor_tag']);
1045 } // resetTagSearch()
1050 * if any name search has taken place, reset it now
1052 public function resetNameSearch()
1054 if(isset($_SESSION['searchfor_name']))
1055 unset($_SESSION['searchfor_name']);
1057 } // resetNameSearch()
1062 * if any date search has taken place, reset it now.
1064 public function resetDateSearch()
1066 if(isset($_SESSION['from_date']))
1067 unset($_SESSION['from_date']);
1068 if(isset($_SESSION['to_date']))
1069 unset($_SESSION['to_date']);
1071 } // resetDateSearch();
1076 * if any rate search has taken place, reset it now.
1078 public function resetRateSearch()
1080 if(isset($_SESSION['rate_from']))
1081 unset($_SESSION['rate_from']);
1082 if(isset($_SESSION['rate_to']))
1083 unset($_SESSION['rate_to']);
1085 } // resetRateSearch();
1088 * return all photo according selection
1090 * this function returns all photos based on
1091 * the tag-selection, tag- or date-search.
1092 * the tag-search also has to take care of AND
1093 * and OR conjunctions
1096 public function getPhotoSelection()
1098 $matched_photos = Array();
1099 $additional_where_cond = "";
1101 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1102 $from_date = $_SESSION['from_date'];
1103 $to_date = $_SESSION['to_date'];
1104 $additional_where_cond.= "
1105 p.time>='". $from_date ."'
1107 p.time<='". $to_date ."'
1111 if(isset($_SESSION['searchfor_name'])) {
1113 /* check for previous conditions. if so add 'AND' */
1114 if(!empty($additional_where_cond)) {
1115 $additional_where_cond.= " AND ";
1118 if($this->dbver < 9) {
1119 $additional_where_cond.= "
1121 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1123 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1128 $additional_where_cond.= "
1130 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1132 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1138 /* limit result based on rate-search */
1139 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1141 if($this->dbver > 10) {
1143 /* check for previous conditions. if so add 'AND' */
1144 if(!empty($additional_where_cond)) {
1145 $additional_where_cond.= " AND ";
1148 $additional_where_cond.= "
1149 p.rating >= ". $_SESSION['rate_from'] ."
1151 p.rating <= ". $_SESSION['rate_to'] ."
1156 if(isset($_SESSION['sort_order'])) {
1157 $order_str = $this->get_sort_order();
1160 /* return a search result */
1161 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1163 SELECT DISTINCT pt1.photo_id
1165 INNER JOIN photo_tags pt2
1166 ON pt1.photo_id=pt2.photo_id
1170 ON pt1.photo_id=p.id
1173 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1175 if(!empty($additional_where_cond))
1176 $query_str.= "AND ". $additional_where_cond ." ";
1178 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1179 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1182 if(isset($order_str))
1183 $query_str.= $order_str;
1185 $result = $this->db->db_query($query_str);
1186 while($row = $this->db->db_fetch_object($result)) {
1187 array_push($matched_photos, $row['photo_id']);
1189 return $matched_photos;
1192 /* return according the selected tags */
1193 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1195 foreach($_SESSION['selected_tags'] as $tag)
1196 $selected.= $tag .",";
1197 $selected = substr($selected, 0, strlen($selected)-1);
1199 /* photo has to match at least on of the selected tags */
1200 if($_SESSION['tag_condition'] == 'or') {
1202 SELECT DISTINCT pt1.photo_id
1204 INNER JOIN photo_tags pt2
1205 ON pt1.photo_id=pt2.photo_id
1209 ON pt1.photo_id=p.id
1210 WHERE pt1.tag_id IN (". $selected .")
1212 if(!empty($additional_where_cond))
1213 $query_str.= "AND ". $additional_where_cond ." ";
1215 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1216 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1219 if(isset($order_str))
1220 $query_str.= $order_str;
1222 /* photo has to match all selected tags */
1223 elseif($_SESSION['tag_condition'] == 'and') {
1225 if(count($_SESSION['selected_tags']) >= 32) {
1226 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1227 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1231 /* Join together a table looking like
1233 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1235 so the query can quickly return all images matching the
1236 selected tags in an AND condition
1241 SELECT DISTINCT pt1.photo_id
1245 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1252 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1254 INNER JOIN photo_tags pt". ($i+2) ."
1255 ON pt1.photo_id=pt". ($i+2) .".photo_id
1260 ON pt1.photo_id=p.id
1262 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1263 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1265 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1268 if(!empty($additional_where_cond))
1269 $query_str.= "AND ". $additional_where_cond;
1271 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1272 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1275 if(isset($order_str))
1276 $query_str.= $order_str;
1280 $result = $this->db->db_query($query_str);
1281 while($row = $this->db->db_fetch_object($result)) {
1282 array_push($matched_photos, $row['photo_id']);
1284 return $matched_photos;
1287 /* return all available photos */
1289 SELECT DISTINCT p.id
1291 LEFT JOIN photo_tags pt
1297 if(!empty($additional_where_cond))
1298 $query_str.= "WHERE ". $additional_where_cond ." ";
1300 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1301 if(!empty($additional_where_cond))
1302 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1304 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1307 if(isset($order_str))
1308 $query_str.= $order_str;
1310 $result = $this->db->db_query($query_str);
1311 while($row = $this->db->db_fetch_object($result)) {
1312 array_push($matched_photos, $row['id']);
1314 return $matched_photos;
1316 } // getPhotoSelection()
1319 * control HTML ouput for photo index
1321 * this function provides all the necessary information
1322 * for the photo index template.
1325 public function showPhotoIndex()
1327 $photos = $this->getPhotoSelection();
1328 $current_tags = $this->getCurrentTags();
1330 $count = count($photos);
1332 /* if all thumbnails should be shown on one page */
1333 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1337 /* thumbnails should be splitted up in several pages */
1338 elseif($this->cfg->thumbs_per_page > 0) {
1340 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1344 $begin_with = $_SESSION['begin_with'];
1347 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1351 $images[$thumbs] = Array();
1352 $img_height[$thumbs] = Array();
1353 $img_width[$thumbs] = Array();
1354 $img_id[$thumbs] = Array();
1355 $img_name[$thumbs] = Array();
1356 $img_fullname[$thumbs] = Array();
1357 $img_title = Array();
1358 $img_rating = Array();
1360 for($i = $begin_with; $i < $end_with; $i++) {
1362 if(isset($photos[$i])) {
1364 $images[$thumbs] = $photos[$i];
1365 $img_id[$thumbs] = $i;
1366 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1367 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1368 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1369 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1371 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1373 if(file_exists($thumb_path)) {
1374 $info = getimagesize($thumb_path);
1375 $img_width[$thumbs] = $info[0];
1376 $img_height[$thumbs] = $info[1];
1382 // +1 for for smarty's selection iteration
1385 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1386 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1388 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1389 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1390 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1393 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1394 $this->tmpl->assign('tag_result', 1);
1397 /* do we have to display the page selector ? */
1398 if($this->cfg->thumbs_per_page != 0) {
1402 /* calculate the page switchers */
1403 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1404 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1406 if($begin_with != 0)
1407 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1408 if($end_with < $count)
1409 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1411 $photo_per_page = $this->cfg->thumbs_per_page;
1412 $last_page = ceil($count / $photo_per_page);
1414 /* get the current selected page */
1415 if($begin_with == 0) {
1419 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1426 for($i = 1; $i <= $last_page; $i++) {
1428 if($current_page == $i)
1429 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1430 elseif($current_page-1 == $i || $current_page+1 == $i)
1431 $style = "style=\"font-size: 105%;\"";
1432 elseif(($current_page-5 >= $i) && ($i != 1) ||
1433 ($current_page+5 <= $i) && ($i != $last_page))
1434 $style = "style=\"font-size: 75%;\"";
1438 $start_with = ($i*$photo_per_page)-$photo_per_page;
1440 if($this->is_user_friendly_url()) {
1441 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1444 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1446 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1450 $select.= ">". $i ."</a> ";
1452 // until 9 pages we show the selector from 1-9
1453 if($last_page <= 9) {
1454 $page_select.= $select;
1457 if($i == 1 /* first page */ ||
1458 $i == $last_page /* last page */ ||
1459 $i == $current_page /* current page */ ||
1460 $i == ceil($last_page * 0.25) /* first quater */ ||
1461 $i == ceil($last_page * 0.5) /* half */ ||
1462 $i == ceil($last_page * 0.75) /* third quater */ ||
1463 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1464 (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 */ ||
1465 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1466 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1468 $page_select.= $select;
1476 $page_select.= "......... ";
1481 /* only show the page selector if we have more then one page */
1483 $this->tmpl->assign('page_selector', $page_select);
1486 $extern_link = "index.php?mode=showpi";
1487 $rss_link = "index.php?mode=rss";
1488 if($current_tags != "") {
1489 $extern_link.= "&tags=". $current_tags;
1490 $rss_link.= "&tags=". $current_tags;
1492 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1493 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1494 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1497 $export_link = "index.php?mode=export";
1498 $slideshow_link = "index.php?mode=slideshow";
1500 $this->tmpl->assign('extern_link', $extern_link);
1501 $this->tmpl->assign('slideshow_link', $slideshow_link);
1502 $this->tmpl->assign('export_link', $export_link);
1503 $this->tmpl->assign('rss_link', $rss_link);
1504 $this->tmpl->assign('count', $count);
1505 $this->tmpl->assign('width', $this->cfg->thumb_width);
1506 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1507 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1508 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1509 $this->tmpl->assign('images', $images);
1510 $this->tmpl->assign('img_width', $img_width);
1511 $this->tmpl->assign('img_height', $img_height);
1512 $this->tmpl->assign('img_id', $img_id);
1513 $this->tmpl->assign('img_name', $img_name);
1514 $this->tmpl->assign('img_fullname', $img_fullname);
1515 $this->tmpl->assign('img_title', $img_title);
1516 $this->tmpl->assign('img_rating', $img_rating);
1517 $this->tmpl->assign('thumbs', $thumbs);
1518 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1520 $result = $this->tmpl->fetch("photo_index.tpl");
1522 /* if we are returning to photo index from an photo-view,
1523 scroll the window to the last shown photo-thumbnail.
1524 after this, unset the last_photo session variable.
1526 if(isset($_SESSION['last_photo'])) {
1527 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1528 unset($_SESSION['last_photo']);
1533 } // showPhotoIndex()
1536 * show credit template
1538 public function showCredits()
1540 $this->tmpl->assign('version', $this->cfg->version);
1541 $this->tmpl->assign('product', $this->cfg->product);
1542 $this->tmpl->assign('db_version', $this->dbver);
1543 $this->tmpl->show("credits.tpl");
1548 * create thumbnails for the requested width
1550 * this function creates image thumbnails of $orig_image
1551 * stored as $thumb_image. It will check if the image is
1552 * in a supported format, if necessary rotate the image
1553 * (based on EXIF orientation meta headers) and re-sizing.
1554 * @param string $orig_image
1555 * @param string $thumb_image
1556 * @param integer $width
1559 public function create_thumbnail($orig_image, $thumb_image, $width)
1561 if(!file_exists($orig_image)) {
1565 $mime = $this->get_mime_info($orig_image);
1567 /* check if original photo is a support image type */
1568 if(!$this->checkifImageSupported($mime))
1575 $meta = $this->get_meta_informations($orig_image);
1581 if(isset($meta['Orientation'])) {
1582 switch($meta['Orientation']) {
1583 case 1: /* top, left */
1584 /* nothing to do */ break;
1585 case 2: /* top, right */
1586 $rotate = 0; $flip_hori = true; break;
1587 case 3: /* bottom, left */
1588 $rotate = 180; break;
1589 case 4: /* bottom, right */
1590 $flip_vert = true; break;
1591 case 5: /* left side, top */
1592 $rotate = 90; $flip_vert = true; break;
1593 case 6: /* right side, top */
1594 $rotate = 90; break;
1595 case 7: /* left side, bottom */
1596 $rotate = 270; $flip_vert = true; break;
1597 case 8: /* right side, bottom */
1598 $rotate = 270; break;
1602 $src_img = @imagecreatefromjpeg($orig_image);
1608 $src_img = @imagecreatefrompng($orig_image);
1612 case 'image/x-portable-pixmap':
1614 $src_img = new Imagick($orig_image);
1615 $handler = "imagick";
1620 if(!isset($src_img) || empty($src_img)) {
1621 print "Can't load image from ". $orig_image ."\n";
1629 /* grabs the height and width */
1630 $cur_width = imagesx($src_img);
1631 $cur_height = imagesy($src_img);
1633 // If requested width is more then the actual image width,
1634 // do not generate a thumbnail, instead safe the original
1635 // as thumbnail but with lower quality. But if the image
1636 // is to heigh too, then we still have to resize it.
1637 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1638 $result = imagejpeg($src_img, $thumb_image, 75);
1639 imagedestroy($src_img);
1646 $cur_width = $src_img->getImageWidth();
1647 $cur_height = $src_img->getImageHeight();
1649 // If requested width is more then the actual image width,
1650 // do not generate a thumbnail, instead safe the original
1651 // as thumbnail but with lower quality. But if the image
1652 // is to heigh too, then we still have to resize it.
1653 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1654 $src_img->setCompressionQuality(75);
1655 $src_img->setImageFormat('jpeg');
1656 $src_img->writeImage($thumb_image);
1658 $src_img->destroy();
1665 // If the image will be rotate because EXIF orientation said so
1666 // 'virtually rotate' the image for further calculations
1667 if($rotate == 90 || $rotate == 270) {
1669 $cur_width = $cur_height;
1673 /* calculates aspect ratio */
1674 $aspect_ratio = $cur_height / $cur_width;
1677 if($aspect_ratio < 1) {
1679 $new_h = abs($new_w * $aspect_ratio);
1681 /* 'virtually' rotate the image and calculate it's ratio */
1682 $tmp_w = $cur_height;
1683 $tmp_h = $cur_width;
1684 /* now get the ratio from the 'rotated' image */
1685 $tmp_ratio = $tmp_h/$tmp_w;
1686 /* now calculate the new dimensions */
1688 $tmp_h = abs($tmp_w * $tmp_ratio);
1690 // now that we know, how high they photo should be, if it
1691 // gets rotated, use this high to scale the image
1693 $new_w = abs($new_h / $aspect_ratio);
1695 // If the image will be rotate because EXIF orientation said so
1696 // now 'virtually rotate' back the image for the image manipulation
1697 if($rotate == 90 || $rotate == 270) {
1708 /* creates new image of that size */
1709 $dst_img = imagecreatetruecolor($new_w, $new_h);
1711 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1713 /* copies resized portion of original image into new image */
1714 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1716 /* needs the image to be flipped horizontal? */
1718 $this->_debug("(FLIP)");
1719 $dst_img = $this->flipImage($dst_img, 'hori');
1721 /* needs the image to be flipped vertical? */
1723 $this->_debug("(FLIP)");
1724 $dst_img = $this->flipImage($dst_img, 'vert');
1728 $this->_debug("(ROTATE)");
1729 $dst_img = $this->rotateImage($dst_img, $rotate);
1732 /* write down new generated file */
1733 $result = imagejpeg($dst_img, $thumb_image, 75);
1735 /* free your mind */
1736 imagedestroy($dst_img);
1737 imagedestroy($src_img);
1739 if($result === false) {
1740 print "Can't write thumbnail ". $thumb_image ."\n";
1750 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1752 /* needs the image to be flipped horizontal? */
1754 $this->_debug("(FLIP)");
1755 $src_img->rotateImage(new ImagickPixel(), 90);
1756 $src_img->flipImage();
1757 $src_img->rotateImage(new ImagickPixel(), -90);
1759 /* needs the image to be flipped vertical? */
1761 $this->_debug("(FLIP)");
1762 $src_img->flipImage();
1766 $this->_debug("(ROTATE)");
1767 $src_img->rotateImage(new ImagickPixel(), $rotate);
1770 $src_img->setCompressionQuality(75);
1771 $src_img->setImageFormat('jpeg');
1773 if(!$src_img->writeImage($thumb_image)) {
1774 print "Can't write thumbnail ". $thumb_image ."\n";
1779 $src_img->destroy();
1786 } // create_thumbnail()
1789 * return all exif meta data from the file
1790 * @param string $file
1793 public function get_meta_informations($file)
1795 return exif_read_data($file);
1797 } // get_meta_informations()
1800 * create phpfspot own sqlite database
1802 * this function creates phpfspots own sqlite database
1803 * if it does not exist yet. this own is used to store
1804 * some necessary informations (md5 sum's, ...).
1806 public function check_config_table()
1808 // if the config table doesn't exist yet, create it
1809 if(!$this->cfg_db->db_check_table_exists("images")) {
1810 $this->cfg_db->db_exec("
1811 CREATE TABLE images (
1812 img_idx int primary key,
1818 } // check_config_table
1821 * Generates a thumbnail from photo idx
1823 * This function will generate JPEG thumbnails from provided F-Spot photo
1826 * 1. Check if all thumbnail generations (width) are already in place and
1828 * 2. Check if the md5sum of the original file has changed
1829 * 3. Generate the thumbnails if needed
1830 * @param integer $idx
1831 * @param integer $force
1832 * @param boolean $overwrite
1834 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1838 $resolutions = Array(
1839 $this->cfg->thumb_width,
1840 $this->cfg->photo_width,
1841 $this->cfg->mini_width,
1845 /* get details from F-Spot's database */
1846 $details = $this->get_photo_details($idx);
1848 /* calculate file MD5 sum */
1849 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1851 if(!file_exists($full_path)) {
1852 $this->_error("File ". $full_path ." does not exist\n");
1856 if(!is_readable($full_path)) {
1857 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1861 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1863 /* If Nikon NEF format, we need to treat it another way */
1864 if(isset($this->cfg->dcraw_bin) &&
1865 file_exists($this->cfg->dcraw_bin) &&
1866 is_executable($this->cfg->dcraw_bin) &&
1867 preg_match('/\.nef$/i', $details['uri'])) {
1869 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1871 /* if PPM file does not exist, let dcraw convert it from NEF */
1872 if(!file_exists($ppm_path)) {
1873 system($this->cfg->dcraw_bin ." -a ". $full_path);
1876 /* for now we handle the PPM instead of the NEF */
1877 $full_path = $ppm_path;
1881 $file_md5 = md5_file($full_path);
1884 foreach($resolutions as $resolution) {
1886 $generate_it = false;
1888 $thumb_sub_path = substr($file_md5, 0, 2);
1889 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1891 /* if thumbnail-subdirectory does not exist yet, create it */
1892 if(!file_exists(dirname($thumb_path))) {
1893 mkdir(dirname($thumb_path), 0755);
1896 /* if the thumbnail file doesn't exist, create it */
1897 if(!file_exists($thumb_path)) {
1898 $generate_it = true;
1900 /* if the file hasn't changed there is no need to regen the thumb */
1901 elseif($file_md5 != $this->getMD5($idx) || $force) {
1902 $generate_it = true;
1905 if($generate_it || $overwrite) {
1907 $this->_debug(" ". $resolution ."px");
1908 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1916 $this->_debug(" already exist");
1919 /* set the new/changed MD5 sum for the current photo */
1921 $this->setMD5($idx, $file_md5);
1924 $this->_debug("\n");
1929 * returns stored md5 sum for a specific photo
1931 * this function queries the phpfspot database for a
1932 * stored MD5 checksum of the specified photo
1933 * @param integer $idx
1934 * @return string|null
1936 public function getMD5($idx)
1938 $result = $this->cfg_db->db_query("
1941 WHERE img_idx='". $idx ."'
1947 $img = $this->cfg_db->db_fetch_object($result);
1948 return $img['img_md5'];
1953 * set MD5 sum for the specific photo
1954 * @param integer $idx
1955 * @param string $md5
1957 private function setMD5($idx, $md5)
1959 $result = $this->cfg_db->db_exec("
1960 REPLACE INTO images (img_idx, img_md5)
1961 VALUES ('". $idx ."', '". $md5 ."')
1967 * store current tag condition
1969 * this function stores the current tag condition
1970 * (AND or OR) in the users session variables
1971 * @param string $mode
1974 public function setTagCondition($mode)
1976 $_SESSION['tag_condition'] = $mode;
1980 } // setTagCondition()
1983 * invoke tag & date search
1985 * this function will return all matching tags and store
1986 * them in the session variable selected_tags. furthermore
1987 * it also handles the date search.
1988 * getPhotoSelection() will then only return the matching
1992 public function startSearch()
1995 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1996 $from = $_POST['from'];
1998 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
2002 /* tag-name search */
2003 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2004 $searchfor_tag = $_POST['for_tag'];
2005 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2008 unset($_SESSION['searchfor_tag']);
2011 /* file-name search */
2012 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2013 $_SESSION['searchfor_name'] = $_POST['for_name'];
2016 unset($_SESSION['searchfor_name']);
2020 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2022 $_SESSION['rate_from'] = $_POST['rate_from'];
2024 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2025 $_SESSION['rate_to'] = $_POST['rate_to'];
2029 /* delete any previously set value */
2030 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2035 if(isset($from) && !empty($from))
2036 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
2038 unset($_SESSION['from_date']);
2040 if(isset($to) && !empty($to))
2041 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
2043 unset($_SESSION['to_date']);
2045 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2046 /* new search, reset the current selected tags */
2047 $_SESSION['selected_tags'] = Array();
2048 foreach($this->avail_tags as $tag) {
2049 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2050 array_push($_SESSION['selected_tags'], $tag);
2059 * updates sort order in session variable
2061 * this function is invoked by RPC and will sort the requested
2062 * sort order in the session variable.
2063 * @param string $sort_order
2066 public function updateSortOrder($order)
2068 if(isset($this->sort_orders[$order])) {
2069 $_SESSION['sort_order'] = $order;
2073 return "unkown error";
2075 } // updateSortOrder()
2080 * this function rotates the image according the
2082 * @param string $img
2083 * @param integer $degress
2086 private function rotateImage($img, $degrees)
2088 if(function_exists("imagerotate")) {
2089 $img = imagerotate($img, $degrees, 0);
2091 function imagerotate($src_img, $angle)
2093 $src_x = imagesx($src_img);
2094 $src_y = imagesy($src_img);
2095 if ($angle == 180) {
2099 elseif ($src_x <= $src_y) {
2103 elseif ($src_x >= $src_y) {
2108 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2109 imagealphablending($rotate, false);
2114 for ($y = 0; $y < ($src_y); $y++) {
2115 for ($x = 0; $x < ($src_x); $x++) {
2116 $color = imagecolorat($src_img, $x, $y);
2117 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2123 for ($y = 0; $y < ($src_y); $y++) {
2124 for ($x = 0; $x < ($src_x); $x++) {
2125 $color = imagecolorat($src_img, $x, $y);
2126 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2132 for ($y = 0; $y < ($src_y); $y++) {
2133 for ($x = 0; $x < ($src_x); $x++) {
2134 $color = imagecolorat($src_img, $x, $y);
2135 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2149 $img = imagerotate($img, $degrees);
2158 * returns flipped image
2160 * this function will return an either horizontal or
2161 * vertical flipped truecolor image.
2162 * @param string $image
2163 * @param string $mode
2166 private function flipImage($image, $mode)
2168 $w = imagesx($image);
2169 $h = imagesy($image);
2170 $flipped = imagecreatetruecolor($w, $h);
2174 for ($y = 0; $y < $h; $y++) {
2175 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2179 for ($x = 0; $x < $w; $x++) {
2180 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2190 * return all assigned tags for the specified photo
2191 * @param integer $idx
2194 private function get_photo_tags($idx)
2196 $result = $this->db->db_query("
2199 INNER JOIN photo_tags pt
2201 WHERE pt.photo_id='". $idx ."'
2206 while($row = $this->db->db_fetch_object($result)) {
2207 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2209 $tags[$row['id']] = $row['name'];
2214 } // get_photo_tags()
2217 * create on-the-fly images with text within
2218 * @param string $txt
2219 * @param string $color
2220 * @param integer $space
2221 * @param integer $font
2224 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2226 if (strlen($color) != 6)
2229 $int = hexdec($color);
2230 $h = imagefontheight($font);
2231 $fw = imagefontwidth($font);
2232 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2233 $lines = count($txt);
2234 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2235 $bg = imagecolorallocate($im, 255, 255, 255);
2236 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2239 foreach ($txt as $text) {
2240 $x = (($w - ($fw * strlen($text))) / 2);
2241 imagestring($im, $font, $x, $y, $text, $color);
2242 $y += ($h + $space);
2245 Header("Content-type: image/png");
2248 } // showTextImage()
2251 * check if all requirements are met
2254 private function check_requirements()
2256 if(!function_exists("imagecreatefromjpeg")) {
2257 print "PHP GD library extension is missing<br />\n";
2261 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2262 print "PHP SQLite3 library extension is missing<br />\n";
2266 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2267 ini_set('track_errors', 1);
2268 @include_once 'HTML/AJAX/Server.php';
2269 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2270 print "PEAR HTML_AJAX package is missing<br />\n";
2273 @include_once 'Calendar/Calendar.php';
2274 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2275 print "PEAR Calendar package is missing<br />\n";
2278 @include_once 'Console/Getopt.php';
2279 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2280 print "PEAR Console_Getopt package is missing<br />\n";
2283 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2284 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2285 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2288 ini_restore('track_errors');
2295 } // check_requirements()
2297 private function _debug($text)
2299 if(isset($this->fromcmd)) {
2306 * check if specified MIME type is supported
2307 * @param string $mime
2310 public function checkifImageSupported($mime)
2312 $supported_types = Array(
2315 "image/x-portable-pixmap",
2319 if(in_array($mime, $supported_types))
2324 } // checkifImageSupported()
2328 * @param string $text
2330 public function _error($text)
2332 switch($this->cfg->logging) {
2335 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2336 print $text ."<br />\n";
2342 error_log($text, 3, $his->cfg->log_file);
2346 $this->runtime_error = true;
2351 * output calendard input fields
2352 * @param string $mode
2355 private function get_calendar($mode)
2357 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2358 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2359 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2361 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2362 if(!isset($_SESSION[$mode .'_date']))
2363 $output.= " disabled=\"disabled\"";
2365 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2366 if(!isset($_SESSION[$mode .'_date']))
2367 $output.= " disabled=\"disabled\"";
2369 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2370 if(!isset($_SESSION[$mode .'_date']))
2371 $output.= " disabled=\"disabled\"";
2379 * output calendar matrix
2380 * @param integer $year
2381 * @param integer $month
2382 * @param integer $day
2384 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2386 if (!isset($year)) $year = date('Y');
2387 if (!isset($month)) $month = date('m');
2388 if (!isset($day)) $day = date('d');
2393 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2394 require_once CALENDAR_ROOT.'Day.php';
2397 $month = new Calendar_Month_Weekdays($year,$month);
2400 $prevStamp = $month->prevMonth(true);
2401 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2402 $nextStamp = $month->nextMonth(true);
2403 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2405 $selectedDays = array (
2406 new Calendar_Day($year,$month,$day),
2407 new Calendar_Day($year,12,25),
2410 // Build the days in the month
2411 $month->build($selectedDays);
2413 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2414 $this->tmpl->assign('prev_month', $prev);
2415 $this->tmpl->assign('next_month', $next);
2417 while ( $day = $month->fetch() ) {
2419 if(!isset($matrix[$rows]))
2420 $matrix[$rows] = Array();
2424 $dayStamp = $day->thisDay(true);
2425 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2427 // isFirst() to find start of week
2428 if ( $day->isFirst() )
2431 if ( $day->isSelected() ) {
2432 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2433 } else if ( $day->isEmpty() ) {
2434 $string.= "<td> </td>\n";
2436 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2439 // isLast() to find end of week
2440 if ( $day->isLast() )
2441 $string.= "</tr>\n";
2443 $matrix[$rows][$cols] = $string;
2453 $this->tmpl->assign('matrix', $matrix);
2454 $this->tmpl->assign('rows', $rows);
2455 $this->tmpl->show("calendar.tpl");
2457 } // get_calendar_matrix()
2460 * output export page
2461 * @param string $mode
2463 public function getExport($mode)
2465 $pictures = $this->getPhotoSelection();
2466 $current_tags = $this->getCurrentTags();
2468 foreach($pictures as $picture) {
2470 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2471 if($current_tags != "") {
2472 $orig_url.= "&tags=". $current_tags;
2474 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2475 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2478 if($this->is_user_friendly_url()) {
2479 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2482 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2488 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2489 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2493 // "[%pictureurl% %thumbnailurl%]"
2494 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2497 case 'MoinMoinList':
2498 // " * [%pictureurl% %thumbnailurl%]"
2499 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2510 public function getRSSFeed()
2512 Header("Content-type: text/xml; charset=utf-8");
2513 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2516 xmlns:media="http://search.yahoo.com/mrss/"
2517 xmlns:dc="http://purl.org/dc/elements/1.1/"
2520 <title>phpfspot</title>
2521 <description>phpfspot RSS feed</description>
2522 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2523 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2524 <generator>phpfspot</generator>
2527 $pictures = $this->getPhotoSelection();
2528 $current_tags = $this->getCurrentTags();
2530 foreach($pictures as $picture) {
2532 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2533 if($current_tags != "") {
2534 $orig_url.= "&tags=". $current_tags;
2536 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2537 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2540 $details = $this->get_photo_details($picture);
2542 if($this->is_user_friendly_url()) {
2543 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2546 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2549 $thumb_html = htmlspecialchars("
2550 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2552 ". $details['description']);
2554 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2556 /* get EXIF information if JPEG */
2557 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2558 $meta = $this->get_meta_informations($orig_path);
2561 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2565 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2566 <link><?php print htmlspecialchars($orig_url); ?></link>
2567 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2568 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2570 <?php print $thumb_html; ?>
2572 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2587 * get all selected tags
2589 * This function will return all selected tags as one string, seperated
2593 private function getCurrentTags()
2596 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2597 foreach($_SESSION['selected_tags'] as $tag)
2598 $current_tags.= $tag .",";
2599 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2601 return $current_tags;
2603 } // getCurrentTags()
2606 * return the current photo
2608 public function getCurrentPhoto()
2610 if(isset($_SESSION['current_photo'])) {
2611 print $_SESSION['current_photo'];
2613 } // getCurrentPhoto()
2616 * tells the client browser what to do
2618 * this function is getting called via AJAX by the
2619 * client browsers. it will tell them what they have
2620 * to do next. This is necessary for directly jumping
2621 * into photo index or single photo view when the are
2622 * requested with specific URLs
2625 public function whatToDo()
2627 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2629 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2630 return "showpi_tags";
2632 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2639 * return the current process-user
2642 private function getuid()
2644 if($uid = posix_getuid()) {
2645 if($user = posix_getpwuid($uid)) {
2646 return $user['name'];
2655 * returns a select-dropdown box to select photo index sort parameters
2656 * @param array $params
2657 * @param smarty $smarty
2660 public function smarty_sort_select_list($params, &$smarty)
2664 foreach($this->sort_orders as $key => $value) {
2665 $output.= "<option value=\"". $key ."\"";
2666 if($key == $_SESSION['sort_order']) {
2667 $output.= " selected=\"selected\"";
2669 $output.= ">". $value ."</option>";
2674 } // smarty_sort_select_list()
2677 * returns the currently selected sort order
2680 private function get_sort_order()
2682 switch($_SESSION['sort_order']) {
2684 return " ORDER BY p.time ASC";
2687 return " ORDER BY p.time DESC";
2690 if($this->dbver < 9) {
2691 return " ORDER BY p.name ASC";
2694 return " ORDER BY basename(p.uri) ASC";
2698 if($this->dbver < 9) {
2699 return " ORDER BY p.name DESC";
2702 return " ORDER BY basename(p.uri) DESC";
2706 return " ORDER BY t.name ASC ,p.time ASC";
2709 return " ORDER BY t.name DESC ,p.time ASC";
2712 return " ORDER BY t.name ASC, p.rating ASC";
2715 return " ORDER BY t.name DESC, p.rating DESC";
2719 } // get_sort_order()
2722 * return the next to be shown slide show image
2724 * this function returns the URL of the next image
2725 * in the slideshow sequence.
2728 public function getNextSlideShowImage()
2730 $all_photos = $this->getPhotoSelection();
2732 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2733 $_SESSION['slideshow_img'] = 0;
2735 $_SESSION['slideshow_img']++;
2737 if($this->is_user_friendly_url()) {
2738 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2741 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2743 } // getNextSlideShowImage()
2746 * return the previous to be shown slide show image
2748 * this function returns the URL of the previous image
2749 * in the slideshow sequence.
2752 public function getPrevSlideShowImage()
2754 $all_photos = $this->getPhotoSelection();
2756 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2757 $_SESSION['slideshow_img'] = 0;
2759 $_SESSION['slideshow_img']--;
2761 if($this->is_user_friendly_url()) {
2762 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2765 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2767 } // getPrevSlideShowImage()
2769 public function resetSlideShow()
2771 if(isset($_SESSION['slideshow_img']))
2772 unset($_SESSION['slideshow_img']);
2774 } // resetSlideShow()
2779 * this function will get all photos from the fspot
2780 * database and randomly return ONE entry
2782 * saddly there is yet no sqlite3 function which returns
2783 * the bulk result in array, so we have to fill up our
2787 public function get_random_photo()
2796 /* if show_tags is set, only return details for photos which
2797 are specified to be shown
2799 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2801 INNER JOIN photo_tags pt
2806 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2809 $result = $this->db->db_query($query_str);
2811 while($row = $this->db->db_fetch_object($result)) {
2812 array_push($all, $row['id']);
2815 return $all[array_rand($all)];
2817 } // get_random_photo()
2820 * get random photo tag photo
2822 * this function will get all photos tagged with the requested
2823 * tag from the fspot database and randomly return ONE entry
2825 * saddly there is yet no sqlite3 function which returns
2826 * the bulk result in array, so we have to fill up our
2830 public function get_random_tag_photo($tagidx)
2837 INNER JOIN photo_tags pt
2841 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2849 pt.tag_id LIKE '". $tagidx ."'
2852 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2855 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2859 $result = $this->db->db_query($query_str);
2861 while($row = $this->db->db_fetch_object($result)) {
2862 array_push($all, $row['id']);
2865 return $all[array_rand($all)];
2867 } // get_random_tag_photo()
2870 * validates provided date
2872 * this function validates if the provided date
2873 * contains a valid date and will return true
2875 * @param string $date_str
2878 public function isValidDate($date_str)
2880 $timestamp = strtotime($date_str);
2882 if(is_numeric($timestamp))
2890 * timestamp to string conversion
2891 * @param integer $timestamp
2894 private function ts2str($timestamp)
2896 if(!empty($timestamp) && is_numeric($timestamp))
2897 return strftime("%Y-%m-%d", $timestamp);
2902 * extract tag-names from $_GET['tags']
2903 * @param string $tags_str
2906 private function extractTags($tags_str)
2908 $not_validated = split(',', $tags_str);
2909 $validated = array();
2911 foreach($not_validated as $tag) {
2912 if(is_numeric($tag))
2913 array_push($validated, $tag);
2921 * returns the full path to a thumbnail
2922 * @param integer $width
2923 * @param integer $photo
2926 public function get_thumb_path($width, $photo)
2928 $md5 = $this->getMD5($photo);
2929 $sub_path = substr($md5, 0, 2);
2930 return $this->cfg->thumb_path
2938 } // get_thumb_path()
2941 * returns server's virtual host name
2944 private function get_server_name()
2946 return $_SERVER['SERVER_NAME'];
2947 } // get_server_name()
2950 * returns type of webprotocol which is currently used
2953 private function get_web_protocol()
2955 if(!isset($_SERVER['HTTPS']))
2959 } // get_web_protocol()
2962 * return url to this phpfspot installation
2965 private function get_phpfspot_url()
2967 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2969 } // get_phpfspot_url()
2972 * returns the number of photos which are tagged with $tag_id
2973 * @param integer $tag_id
2976 public function get_num_photos($tag_id)
2978 if($result = $this->db->db_fetchSingleRow("
2979 SELECT count(*) as number
2982 tag_id LIKE '". $tag_id ."'")) {
2984 return $result['number'];
2990 } // get_num_photos()
2993 * check file exists and is readable
2995 * returns true, if everything is ok, otherwise false
2996 * if $silent is not set, this function will output and
2998 * @param string $file
2999 * @param boolean $silent
3002 private function check_readable($file, $silent = null)
3004 if(!file_exists($file)) {
3006 print "File \"". $file ."\" does not exist.\n";
3010 if(!is_readable($file)) {
3012 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3018 } // check_readable()
3021 * check if all needed indices are present
3023 * this function checks, if some needed indices are already
3024 * present, or if not, create them on the fly. they are
3025 * necessary to speed up some queries like that one look for
3026 * all tags, when show_tags is specified in the configuration.
3028 private function checkDbIndices()
3030 $result = $this->db->db_exec("
3031 CREATE INDEX IF NOT EXISTS
3038 } // checkDbIndices()
3041 * retrive F-Spot database version
3043 * this function will return the F-Spot database version number
3044 * It is stored within the sqlite3 database in the table meta
3045 * @return string|null
3047 public function getFspotDBVersion()
3049 if($result = $this->db->db_fetchSingleRow("
3050 SELECT data as version
3053 name LIKE 'F-Spot Database Version'
3055 return $result['version'];
3059 } // getFspotDBVersion()
3062 * parse the provided URI and will returned the requested chunk
3063 * @param string $uri
3064 * @param string $mode
3067 public function parse_uri($uri, $mode)
3069 if(($components = parse_url($uri)) !== false) {
3073 return basename($components['path']);
3076 return dirname($components['path']);
3079 return $components['path'];
3089 * validate config options
3091 * this function checks if all necessary configuration options are
3092 * specified and set.
3095 private function check_config_options()
3097 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3098 $this->_error("Please set \$page_title in phpfspot_cfg");
3100 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3101 $this->_error("Please set \$base_path in phpfspot_cfg");
3103 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3104 $this->_error("Please set \$web_path in phpfspot_cfg");
3106 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3107 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3109 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3110 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3112 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3113 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3115 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3116 $this->_error("Please set \$db_access in phpfspot_cfg");
3118 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3119 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3121 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3122 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3124 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3125 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3127 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3128 $this->_error("Please set \$photo_width in phpfspot_cfg");
3130 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3131 $this->_error("Please set \$mini_width in phpfspot_cfg");
3133 if(!isset($this->cfg->thumbs_per_page))
3134 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3136 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3137 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3139 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3140 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3142 if(!isset($this->cfg->hide_tags))
3143 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3145 if(!isset($this->cfg->theme_name))
3146 $this->_error("Please set \$theme_name in phpfspot_cfg");
3148 if(!isset($this->cfg->logging))
3149 $this->_error("Please set \$logging in phpfspot_cfg");
3151 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3153 if(!isset($this->cfg->log_file))
3154 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3156 if(!is_writeable($this->cfg->log_file))
3157 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3161 /* remove trailing slash, if set */
3162 if($this->cfg->web_path == "/")
3163 $this->cfg->web_path = "";
3164 elseif(preg_match('/\/$/', $this->cfg->web_path))
3165 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3167 return $this->runtime_error;
3169 } // check_config_options()
3172 * cleanup phpfspot own database
3174 * When photos are getting delete from F-Spot, there will remain
3175 * remain some residues in phpfspot own database. This function
3176 * will try to wipe them out.
3178 public function cleanup_phpfspot_db()
3180 $to_delete = Array();
3182 $result = $this->cfg_db->db_query("
3185 ORDER BY img_idx ASC
3188 while($row = $this->cfg_db->db_fetch_object($result)) {
3189 if(!$this->db->db_fetchSingleRow("
3192 WHERE id='". $row['img_idx'] ."'")) {
3194 array_push($to_delete, $row['img_idx'], ',');
3198 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3200 $this->cfg_db->db_exec("
3202 WHERE img_idx IN (". implode($to_delete) .")
3205 } // cleanup_phpfspot_db()
3208 * return first image of the page, the $current photo
3211 * this function is used to find out the first photo of the
3212 * current page, in which the $current photo lies. this is
3213 * used to display the correct photo, when calling showPhotoIndex()
3215 * @param integer $current
3216 * @param integer $max
3219 private function getCurrentPage($current, $max)
3221 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3222 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3223 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3229 } // getCurrentPage()
3234 * this function tries to find out the correct mime-type
3235 * for the provided file.
3236 * @param string $file
3239 public function get_mime_info($file)
3241 $details = getimagesize($file);
3243 /* if getimagesize() returns empty, try at least to find out the
3246 if(empty($details) && function_exists('mime_content_type')) {
3248 // mime_content_type is marked as deprecated in the documentation,
3249 // but is it really necessary to force users to install a PECL
3251 $details['mime'] = mime_content_type($file);
3254 return $details['mime'];
3256 } // get_mime_info()
3259 * return tag-name by tag-idx
3261 * this function returns the tag-name for the requested
3262 * tag specified by tag-idx.
3263 * @param integer $idx
3266 public function get_tag_name($idx)
3268 if($result = $this->db->db_fetchSingleRow("
3272 id LIKE '". $idx ."'")) {
3274 return $result['name'];
3283 * parse user friendly url which got rewritten by the websever
3284 * @param string $request_uri
3287 private function parse_user_friendly_url($request_uri)
3289 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3291 $options = explode('/', $request_uri);
3293 switch($options[1]) {
3295 if(is_numeric($options[2])) {
3296 $this->session_cleanup();
3297 //unset($_SESSION['start_action']);
3298 //unset($_SESSION['selected_tags']);
3299 $_GET['mode'] = 'showp';
3300 return $this->showPhoto($options[2]);
3304 if(is_numeric($options[2])) {
3305 require_once "phpfspot_img.php";
3306 $img = new PHPFSPOT_IMG;
3307 if(isset($options[3]) && is_numeric($options[3]))
3308 $img->showImg($options[2], $options[3]);
3310 $img->showImg($options[2]);
3315 if(is_numeric($options[2])) {
3316 $this->session_cleanup();
3317 $_GET['tags'] = $options[2];
3318 $_SESSION['selected_tags'] = Array($options[2]);
3319 if(isset($options[3]) && is_numeric($options[3]))
3320 $_SESSION['begin_with'] = $options[3];
3321 return $this->showPhotoIndex();
3327 } // parse_user_friendly_url()
3330 * check if user-friendly-urls are enabled
3332 * this function will return true, if the config option
3333 * $user_friendly_url has been set. Otherwise false.
3336 private function is_user_friendly_url()
3338 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3343 } // is_user_friendly_url()
3348 * this function will cleanup user's session information
3350 private function session_cleanup()
3352 unset($_SESSION['begin_with']);
3353 $this->resetDateSearch();
3354 $this->resetPhotoView();
3355 $this->resetTagSearch();
3356 $this->resetNameSearch();
3357 $this->resetDateSearch();
3360 } // session_cleanup()