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.6";
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 "Error: ". $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() .".\n";
146 print "Please fix permissions so phpfspot can create indices within the F-Spot database to"
147 ." speed up some database operations.\n";
151 /* open the database */
152 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
154 /* change sqlite temp directory, if requested */
155 if(isset($this->cfg->sqlite_temp_dir)) {
158 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
162 /* get F-Spot database version */
163 $this->dbver = $this->getFspotDBVersion();
165 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
166 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
170 if(!is_writeable($this->cfg->thumb_path)) {
171 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
175 /******* Opening phpfspot's sqlite database *********/
177 /* Check if directory where the database file is stored is writeable */
178 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
179 print "Error: ". dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() .".\n";
180 print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n";
184 /* Check if database file is writeable */
185 if(!is_writeable($this->cfg->phpfspot_db)) {
186 print "Error: ". $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() .".\n";
187 print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n";
191 /* open the database */
192 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
194 /* change sqlite temp directory, if requested */
195 if(isset($this->cfg->sqlite_temp_dir)) {
196 $this->cfg_db->db_exec("
198 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
202 /* Check if some tables need to be created */
203 $this->check_config_table();
205 /* overload Smarty class with our own template handler */
206 require_once "phpfspot_tmpl.php";
207 $this->tmpl = new PHPFSPOT_TMPL();
209 $this->tmpl->assign('web_path', $this->cfg->web_path);
211 /* Starting with F-Spot 0.4.2, the rating-feature was available */
212 if($this->dbver > 10) {
213 $this->tmpl->assign('has_rating', true);
214 $this->sort_orders = array_merge($this->sort_orders, array(
215 'rate_asc' => 'Rate ↑',
216 'rate_desc' => 'Rate ↓',
220 /* check if all necessary indices exist */
221 $this->checkDbIndices();
223 /* if session is not yet started, do it now */
224 if(session_id() == "")
227 if(!isset($_SESSION['tag_condition']))
228 $_SESSION['tag_condition'] = 'or';
230 /* if sort-order has not been set yet, get the one specified in the config */
231 if(!isset($_SESSION['sort_order']))
232 $_SESSION['sort_order'] = $this->cfg->sort_order;
234 if(!isset($_SESSION['searchfor_tag']))
235 $_SESSION['searchfor_tag'] = '';
237 // if begin_with is still set but thumbs_per_page is now 0, unset it
238 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
239 unset($_SESSION['begin_with']);
241 // if user-friendly-url's are enabled, set also a flag for the template handler
242 if($this->is_user_friendly_url()) {
243 $this->tmpl->assign('user_friendly_url', 'true');
248 public function __destruct()
254 * show - generate html output
256 * this function can be called after the constructor has
257 * prepared everyhing. it will load the index.tpl smarty
258 * template. if necessary it will registere pre-selects
259 * (photo index, photo, tag search, date search) into
262 public function show()
264 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
265 $this->tmpl->assign('page_title', $this->cfg->page_title);
266 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
267 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
270 if($this->is_user_friendly_url()) {
271 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
274 if(isset($_GET['mode'])) {
276 $_SESSION['start_action'] = $_GET['mode'];
278 switch($_GET['mode']) {
280 if(isset($_GET['tags'])) {
281 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
283 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
284 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
286 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
287 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
291 if(isset($_GET['tags'])) {
292 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
293 $_SESSION['start_action'] = 'showp';
295 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
296 $_SESSION['current_photo'] = $_GET['id'];
297 $_SESSION['start_action'] = 'showp';
299 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
300 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
302 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
303 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
307 /* fetch export template */
308 print $this->tmpl->fetch("export.tpl");
309 /* no further execution necessary. */
313 /* fetch slideshow template */
314 print $this->tmpl->show("slideshow.tpl");
315 /* no further execution necessary. */
319 if(isset($_GET['tags'])) {
320 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
322 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
323 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
325 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
326 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
334 /* if date-search variables are registered in the session, set the check
335 for "consider date-range" in the html output
337 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
338 $this->tmpl->assign('date_search_enabled', true);
340 /* if rate-search variables are registered in the session, set the check
341 for "consider rate-range" in the html output
343 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
344 $this->tmpl->assign('rate_search_enabled', true);
347 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
348 $this->tmpl->assign('search_from_date', $this->get_date_text_field('from'));
349 $this->tmpl->assign('search_to_date', $this->get_date_text_field('to'));
351 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
352 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
353 $this->tmpl->assign('rate_search', $this->get_rate_search());
355 /* if no site-content has been set yet... */
356 if(!isset($content)) {
357 /* if tags are already selected, we can immediately display photo-index */
358 if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) &&
359 isset($_SESSION['start_action']) && $_SESSION['start_action'] != 'showp') ||
360 (isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi'))
361 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
363 /* if a photo is already selected, we can immediately display single-photo */
364 if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo']))
365 $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo']));
367 /* ok, then let us show the welcome page... */
368 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
373 $this->tmpl->assign('initial_content', $content);
375 $this->tmpl->show("index.tpl");
380 * get_tags - grab all tags of f-spot's database
382 * this function will get all available tags from
383 * the f-spot database and store them within two
384 * arrays within this class for later usage. in
385 * fact, if the user requests (hide_tags) it will
386 * opt-out some of them.
388 * this function is getting called once by show()
390 private function get_tags()
392 $this->avail_tags = Array();
395 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
398 DISTINCT t1.id as id, t1.name as name
401 INNER JOIN photo_tags
402 pt2 ON pt1.photo_id=pt2.photo_id
408 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
410 t1.sort_priority ASC";
412 $result = $this->db->db_query($query_str);
416 $result = $this->db->db_query("
419 ORDER BY sort_priority ASC
423 while($row = $this->db->db_fetch_object($result)) {
425 $tag_id = $row['id'];
426 $tag_name = $row['name'];
428 /* if the user has specified to ignore this tag in phpfspot's
429 configuration, ignore it here so it does not get added to
432 if(in_array($row['name'], $this->cfg->hide_tags))
435 /* if you include the following if-clause and the user has specified
436 to only show certain tags which are specified in phpfspot's
437 configuration, ignore all others so they will not be added to the
439 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
440 !in_array($row['name'], $this->cfg->show_tags))
444 $this->tags[$tag_id] = $tag_name;
445 $this->avail_tags[$count] = $tag_id;
453 * extract all photo details
455 * retrieve all available details from f-spot's
456 * database and return them as object
457 * @param integer $idx
458 * @return object|null
460 public function get_photo_details($idx)
462 if($this->dbver < 9) {
464 SELECT p.id, p.name, p.time, p.directory_path, p.description
469 /* till F-Spot version 0.4.1 */
470 if($this->dbver < 11) {
472 SELECT p.id, p.uri, p.time, p.description
478 SELECT p.id, p.uri, p.time, p.description, p.rating
484 /* if show_tags is set, only return details for photos which
485 are specified to be shown
487 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
489 INNER JOIN photo_tags pt
493 WHERE p.id='". $idx ."'
494 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
498 WHERE p.id='". $idx ."'
502 if($result = $this->db->db_query($query_str)) {
504 $row = $this->db->db_fetch_object($result);
506 if($this->dbver < 9) {
507 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
516 } // get_photo_details
519 * returns aligned photo names
521 * this function returns aligned (length) names for
522 * an specific photo. If the length of the name exceeds
523 * $limit the name will be shrinked (...)
524 * @param integer $idx
525 * @param integer $limit
526 * @return string|null
528 public function getPhotoName($idx, $limit = 0)
530 if($details = $this->get_photo_details($idx)) {
531 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
532 $name = $this->shrink_text($long_name, $limit);
542 * get photo rating level
544 * this function will return the integer-based rating
545 * level of the photo. This can only be done, if the F-Spot
546 * database is at a specific level. If rating value can not
547 * be found, zero will be returned indicating no rating value
552 public function get_photo_rating($idx)
554 if($detail = $this->get_photo_details($idx)) {
555 if(isset($detail['rating']))
556 return $detail['rating'];
561 } // get_photo_rating()
564 * get rate-search bars
566 * this function will return the rating-bars for the
570 public function get_rate_search()
574 for($i = 1; $i <= 5; $i++) {
576 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
578 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
579 $bar.= $this->cfg->web_path ."/resources/star.png";
581 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
584 onmouseover=\"show_rate('from', ". $i .");\"
585 onmouseout=\"reset_rate('from');\"
586 onclick=\"set_rate('from', ". $i .")\" />";
591 for($i = 1; $i <= 5; $i++) {
593 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
595 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
596 $bar.= $this->cfg->web_path ."/resources/star.png";
598 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
601 onmouseover=\"show_rate('to', ". $i .");\"
602 onmouseout=\"reset_rate('to');\"
603 onclick=\"set_rate('to', ". $i .");\" />";
608 } // get_rate_search()
611 * shrink text according provided limit
613 * If the length of the name exceeds $limit the
614 * text will be shortend and some content in between
615 * will be replaced with "..."
617 * @param integer $limit
620 private function shrink_text($text, $limit)
622 if($limit != 0 && strlen($text) > $limit) {
623 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
631 * translate f-spoth photo path
633 * as the full-qualified path recorded in the f-spot database
634 * is usally not the same as on the webserver, this function
635 * will replace the path with that one specified in the cfg
636 * @param string $path
639 public function translate_path($path)
641 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
646 * control HTML ouput for a single photo
648 * this function provides all the necessary information
649 * for the single photo template.
650 * @param integer photo
652 public function showPhoto($photo)
654 /* get all photos from the current photo selection */
655 $all_photos = $this->getPhotoSelection();
656 $count = count($all_photos);
658 for($i = 0; $i < $count; $i++) {
660 // $get_next will be set, when the photo which has to
661 // be displayed has been found - this means that the
662 // next available is in fact the NEXT image (for the
664 if(isset($get_next)) {
665 $next_img = $all_photos[$i];
669 /* the next photo is our NEXT photo */
670 if($all_photos[$i] == $photo) {
674 $previous_img = $all_photos[$i];
677 if($photo == $all_photos[$i]) {
682 $details = $this->get_photo_details($photo);
689 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
690 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
692 if(!file_exists($orig_path)) {
693 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
697 if(!is_readable($orig_path)) {
698 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
702 /* If the thumbnail doesn't exist yet, try to create it */
703 if(!file_exists($thumb_path)) {
704 $this->gen_thumb($photo, true);
705 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
708 /* get mime-type, height and width from the original photo */
709 $info = getimagesize($orig_path);
711 /* get EXIF information if JPEG */
712 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
713 $meta = $this->get_meta_informations($orig_path);
716 /* If EXIF data are available, use them */
717 if(isset($meta['ExifImageWidth'])) {
718 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
720 $meta_res = $info[0] ."x". $info[1];
723 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
724 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
726 $extern_link = "index.php?mode=showp&id=". $photo;
727 $current_tags = $this->getCurrentTags();
728 if($current_tags != "") {
729 $extern_link.= "&tags=". $current_tags;
731 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
732 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
735 $this->tmpl->assign('extern_link', $extern_link);
737 if(!file_exists($thumb_path)) {
738 $this->_error("Can't open file ". $thumb_path ."\n");
742 $info_thumb = getimagesize($thumb_path);
744 $this->tmpl->assign('description', $details['description']);
745 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
746 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
748 $this->tmpl->assign('width', $info_thumb[0]);
749 $this->tmpl->assign('height', $info_thumb[1]);
750 $this->tmpl->assign('ExifMadeOn', strftime("%a %x %X", $details['time']));
751 $this->tmpl->assign('ExifMadeWith', $meta_make);
752 $this->tmpl->assign('ExifOrigResolution', $meta_res);
753 $this->tmpl->assign('ExifFileSize', $meta_size);
755 if($this->is_user_friendly_url()) {
756 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
757 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
760 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
761 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
764 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
766 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
767 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
768 $this->tmpl->assign('current_img', $photo);
770 if(isset($previous_img)) {
771 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
772 $this->tmpl->assign('prev_img', $previous_img);
775 if(isset($next_img)) {
776 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
777 $this->tmpl->assign('next_img', $next_img);
780 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
781 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
782 $this->tmpl->assign('photo_number', $i);
783 $this->tmpl->assign('photo_count', count($all_photos));
785 return $this->tmpl->fetch("single_photo.tpl");
790 * all available tags and tag cloud
792 * this function outputs all available tags (time ordered)
793 * and in addition output them as tag cloud (tags which have
794 * many photos will appears more then others)
796 public function getAvailableTags()
798 /* retrive tags from database */
803 $result = $this->db->db_query("
804 SELECT tag_id as id, count(tag_id) as quantity
814 while($row = $this->db->db_fetch_object($result)) {
815 $tags[$row['id']] = $row['quantity'];
818 // change these font sizes if you will
819 $max_size = 125; // max font size in %
820 $min_size = 75; // min font size in %
823 $max_sat = hexdec('cc');
824 $min_sat = hexdec('44');
826 // get the largest and smallest array values
827 $max_qty = max(array_values($tags));
828 $min_qty = min(array_values($tags));
830 // find the range of values
831 $spread = $max_qty - $min_qty;
832 if (0 == $spread) { // we don't want to divide by zero
836 // determine the font-size increment
837 // this is the increase per tag quantity (times used)
838 $step = ($max_size - $min_size)/($spread);
839 $step_sat = ($max_sat - $min_sat)/($spread);
841 // loop through our tag array
842 foreach ($tags as $key => $value) {
844 /* has the currently processed tag already been added to
845 the selected tag list? if so, ignore it here...
847 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
850 // calculate CSS font-size
851 // find the $value in excess of $min_qty
852 // multiply by the font-size increment ($size)
853 // and add the $min_size set above
854 $size = $min_size + (($value - $min_qty) * $step);
855 // uncomment if you want sizes in whole %:
858 $color = $min_sat + ($value - $min_qty) * $step_sat;
864 if(isset($this->tags[$key])) {
865 if($this->is_user_friendly_url()) {
866 $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\"
867 onclick=\"Tags('add', ". $key ."); return false;\"
869 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
870 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
873 $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\"
874 onclick=\"Tags('add', ". $key ."); return false;\"
876 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
877 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
882 $output = substr($output, 0, strlen($output)-2);
885 } // getAvailableTags()
888 * output all selected tags
890 * this function output all tags which have been selected
891 * by the user. the selected tags are stored in the
892 * session-variable $_SESSION['selected_tags']
895 public function getSelectedTags($type = 'link')
897 /* retrive tags from database */
902 foreach($this->avail_tags as $tag)
904 // return all selected tags
905 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
910 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
914 <div class=\"tagresulttag\">
915 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
916 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
926 $output = substr($output, 0, strlen($output)-2);
930 return "no tags selected";
933 } // getSelectedTags()
936 * add tag to users session variable
938 * this function will add the specified to users current
939 * tag selection. if a date search has been made before
940 * it will be now cleared
943 public function addTag($tag)
945 if(!isset($_SESSION['selected_tags']))
946 $_SESSION['selected_tags'] = Array();
948 if(isset($_SESSION['searchfor_tag']))
949 unset($_SESSION['searchfor_tag']);
951 // has the user requested to hide this tag, and still someone,
952 // somehow tries to add it, don't allow this.
953 if(!isset($this->cfg->hide_tags) &&
954 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
957 if(!in_array($tag, $_SESSION['selected_tags']))
958 array_push($_SESSION['selected_tags'], $tag);
965 * remove tag to users session variable
967 * this function removes the specified tag from
968 * users current tag selection
972 public function delTag($tag)
974 if(isset($_SESSION['searchfor_tag']))
975 unset($_SESSION['searchfor_tag']);
977 if(isset($_SESSION['selected_tags'])) {
978 $key = array_search($tag, $_SESSION['selected_tags']);
979 unset($_SESSION['selected_tags'][$key]);
980 sort($_SESSION['selected_tags']);
988 * reset tag selection
990 * if there is any tag selection, it will be
993 public function resetTags()
995 if(isset($_SESSION['selected_tags']))
996 unset($_SESSION['selected_tags']);
1001 * returns the value for the autocomplete tag-search
1004 public function get_xml_tag_list()
1006 if(!isset($_GET['search']) || !is_string($_GET['search']))
1007 $_GET['search'] = '';
1012 /* retrive tags from database */
1015 $matched_tags = Array();
1017 header("Content-Type: text/xml");
1019 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1020 $string.= "<results>\n";
1022 foreach($this->avail_tags as $tag)
1024 if(!empty($_GET['search']) &&
1025 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1026 count($matched_tags) < $length) {
1028 $count = $this->get_num_photos($tag);
1031 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1034 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1040 /* if we have collected enough items, break out */
1041 if(count($matched_tags) >= $length)
1045 $string.= "</results>\n";
1049 } // get_xml_tag_list()
1053 * reset single photo
1055 * if a specific photo was requested (external link)
1056 * unset the session variable now
1058 public function resetPhotoView()
1060 if(isset($_SESSION['current_photo']))
1061 unset($_SESSION['current_photo']);
1063 } // resetPhotoView();
1068 * if any tag search has taken place, reset it now
1070 public function resetTagSearch()
1072 if(isset($_SESSION['searchfor_tag']))
1073 unset($_SESSION['searchfor_tag']);
1075 } // resetTagSearch()
1080 * if any name search has taken place, reset it now
1082 public function resetNameSearch()
1084 if(isset($_SESSION['searchfor_name']))
1085 unset($_SESSION['searchfor_name']);
1087 } // resetNameSearch()
1092 * if any date search has taken place, reset it now.
1094 public function resetDateSearch()
1096 if(isset($_SESSION['from_date']))
1097 unset($_SESSION['from_date']);
1098 if(isset($_SESSION['to_date']))
1099 unset($_SESSION['to_date']);
1101 } // resetDateSearch();
1106 * if any rate search has taken place, reset it now.
1108 public function resetRateSearch()
1110 if(isset($_SESSION['rate_from']))
1111 unset($_SESSION['rate_from']);
1112 if(isset($_SESSION['rate_to']))
1113 unset($_SESSION['rate_to']);
1115 } // resetRateSearch();
1118 * return all photo according selection
1120 * this function returns all photos based on
1121 * the tag-selection, tag- or date-search.
1122 * the tag-search also has to take care of AND
1123 * and OR conjunctions
1126 public function getPhotoSelection()
1128 $matched_photos = Array();
1129 $additional_where_cond = "";
1131 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1132 $from_date = $_SESSION['from_date'];
1133 $to_date = $_SESSION['to_date'];
1134 $additional_where_cond.= "
1135 p.time>='". $from_date ."'
1137 p.time<='". $to_date ."'
1141 if(isset($_SESSION['searchfor_name'])) {
1143 /* check for previous conditions. if so add 'AND' */
1144 if(!empty($additional_where_cond)) {
1145 $additional_where_cond.= " AND ";
1148 if($this->dbver < 9) {
1149 $additional_where_cond.= "
1151 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1153 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1158 $additional_where_cond.= "
1160 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1162 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1168 /* limit result based on rate-search */
1169 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1171 if($this->dbver > 10) {
1173 /* check for previous conditions. if so add 'AND' */
1174 if(!empty($additional_where_cond)) {
1175 $additional_where_cond.= " AND ";
1178 $additional_where_cond.= "
1179 p.rating >= ". $_SESSION['rate_from'] ."
1181 p.rating <= ". $_SESSION['rate_to'] ."
1186 if(isset($_SESSION['sort_order'])) {
1187 $order_str = $this->get_sort_order();
1190 /* return a search result */
1191 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1193 SELECT DISTINCT pt1.photo_id
1195 INNER JOIN photo_tags pt2
1196 ON pt1.photo_id=pt2.photo_id
1200 ON pt1.photo_id=p.id
1203 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1205 if(!empty($additional_where_cond))
1206 $query_str.= "AND ". $additional_where_cond ." ";
1208 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1209 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1212 if(isset($order_str))
1213 $query_str.= $order_str;
1215 $result = $this->db->db_query($query_str);
1216 while($row = $this->db->db_fetch_object($result)) {
1217 array_push($matched_photos, $row['photo_id']);
1219 return $matched_photos;
1222 /* return according the selected tags */
1223 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1225 foreach($_SESSION['selected_tags'] as $tag)
1226 $selected.= $tag .",";
1227 $selected = substr($selected, 0, strlen($selected)-1);
1229 /* photo has to match at least on of the selected tags */
1230 if($_SESSION['tag_condition'] == 'or') {
1232 SELECT DISTINCT pt1.photo_id
1234 INNER JOIN photo_tags pt2
1235 ON pt1.photo_id=pt2.photo_id
1239 ON pt1.photo_id=p.id
1240 WHERE pt1.tag_id IN (". $selected .")
1242 if(!empty($additional_where_cond))
1243 $query_str.= "AND ". $additional_where_cond ." ";
1245 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1246 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1249 if(isset($order_str))
1250 $query_str.= $order_str;
1252 /* photo has to match all selected tags */
1253 elseif($_SESSION['tag_condition'] == 'and') {
1255 if(count($_SESSION['selected_tags']) >= 32) {
1256 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1257 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1261 /* Join together a table looking like
1263 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1265 so the query can quickly return all images matching the
1266 selected tags in an AND condition
1271 SELECT DISTINCT pt1.photo_id
1275 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1282 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1284 INNER JOIN photo_tags pt". ($i+2) ."
1285 ON pt1.photo_id=pt". ($i+2) .".photo_id
1290 ON pt1.photo_id=p.id
1292 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1293 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1295 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1298 if(!empty($additional_where_cond))
1299 $query_str.= "AND ". $additional_where_cond;
1301 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1302 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1305 if(isset($order_str))
1306 $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['photo_id']);
1314 return $matched_photos;
1317 /* return all available photos */
1319 SELECT DISTINCT p.id
1321 LEFT JOIN photo_tags pt
1327 if(!empty($additional_where_cond))
1328 $query_str.= "WHERE ". $additional_where_cond ." ";
1330 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1331 if(!empty($additional_where_cond))
1332 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1334 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1337 if(isset($order_str))
1338 $query_str.= $order_str;
1340 $result = $this->db->db_query($query_str);
1341 while($row = $this->db->db_fetch_object($result)) {
1342 array_push($matched_photos, $row['id']);
1344 return $matched_photos;
1346 } // getPhotoSelection()
1349 * control HTML ouput for photo index
1351 * this function provides all the necessary information
1352 * for the photo index template.
1355 public function showPhotoIndex()
1357 $photos = $this->getPhotoSelection();
1358 $current_tags = $this->getCurrentTags();
1360 $count = count($photos);
1362 /* if all thumbnails should be shown on one page */
1363 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1367 /* thumbnails should be splitted up in several pages */
1368 elseif($this->cfg->thumbs_per_page > 0) {
1370 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1374 $begin_with = $_SESSION['begin_with'];
1377 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1381 $images[$thumbs] = Array();
1382 $img_height[$thumbs] = Array();
1383 $img_width[$thumbs] = Array();
1384 $img_id[$thumbs] = Array();
1385 $img_name[$thumbs] = Array();
1386 $img_fullname[$thumbs] = Array();
1387 $img_title = Array();
1388 $img_rating = Array();
1390 for($i = $begin_with; $i < $end_with; $i++) {
1392 if(isset($photos[$i])) {
1394 $images[$thumbs] = $photos[$i];
1395 $img_id[$thumbs] = $i;
1396 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1397 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1398 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1399 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1401 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1403 if(file_exists($thumb_path)) {
1404 $info = getimagesize($thumb_path);
1405 $img_width[$thumbs] = $info[0];
1406 $img_height[$thumbs] = $info[1];
1412 // +1 for for smarty's selection iteration
1415 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1416 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1418 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1419 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1420 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1423 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1424 $this->tmpl->assign('tag_result', 1);
1427 /* do we have to display the page selector ? */
1428 if($this->cfg->thumbs_per_page != 0) {
1432 /* calculate the page switchers */
1433 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1434 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1436 if($begin_with != 0)
1437 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1438 if($end_with < $count)
1439 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1441 $photo_per_page = $this->cfg->thumbs_per_page;
1442 $last_page = ceil($count / $photo_per_page);
1444 /* get the current selected page */
1445 if($begin_with == 0) {
1449 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1456 for($i = 1; $i <= $last_page; $i++) {
1458 if($current_page == $i)
1459 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1460 elseif($current_page-1 == $i || $current_page+1 == $i)
1461 $style = "style=\"font-size: 105%;\"";
1462 elseif(($current_page-5 >= $i) && ($i != 1) ||
1463 ($current_page+5 <= $i) && ($i != $last_page))
1464 $style = "style=\"font-size: 75%;\"";
1468 $start_with = ($i*$photo_per_page)-$photo_per_page;
1470 if($this->is_user_friendly_url()) {
1471 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1474 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1476 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1480 $select.= ">". $i ."</a> ";
1482 // until 9 pages we show the selector from 1-9
1483 if($last_page <= 9) {
1484 $page_select.= $select;
1487 if($i == 1 /* first page */ ||
1488 $i == $last_page /* last page */ ||
1489 $i == $current_page /* current page */ ||
1490 $i == ceil($last_page * 0.25) /* first quater */ ||
1491 $i == ceil($last_page * 0.5) /* half */ ||
1492 $i == ceil($last_page * 0.75) /* third quater */ ||
1493 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1494 (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 */ ||
1495 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1496 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1498 $page_select.= $select;
1506 $page_select.= "......... ";
1511 /* only show the page selector if we have more then one page */
1513 $this->tmpl->assign('page_selector', $page_select);
1516 $extern_link = "index.php?mode=showpi";
1517 $rss_link = "index.php?mode=rss";
1518 if($current_tags != "") {
1519 $extern_link.= "&tags=". $current_tags;
1520 $rss_link.= "&tags=". $current_tags;
1522 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1523 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1524 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1527 $export_link = "index.php?mode=export";
1528 $slideshow_link = "index.php?mode=slideshow";
1530 $this->tmpl->assign('extern_link', $extern_link);
1531 $this->tmpl->assign('slideshow_link', $slideshow_link);
1532 $this->tmpl->assign('export_link', $export_link);
1533 $this->tmpl->assign('rss_link', $rss_link);
1534 $this->tmpl->assign('count', $count);
1535 $this->tmpl->assign('width', $this->cfg->thumb_width);
1536 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1537 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1538 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1539 $this->tmpl->assign('images', $images);
1540 $this->tmpl->assign('img_width', $img_width);
1541 $this->tmpl->assign('img_height', $img_height);
1542 $this->tmpl->assign('img_id', $img_id);
1543 $this->tmpl->assign('img_name', $img_name);
1544 $this->tmpl->assign('img_fullname', $img_fullname);
1545 $this->tmpl->assign('img_title', $img_title);
1546 $this->tmpl->assign('img_rating', $img_rating);
1547 $this->tmpl->assign('thumbs', $thumbs);
1548 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1550 $result = $this->tmpl->fetch("photo_index.tpl");
1552 /* if we are returning to photo index from an photo-view,
1553 scroll the window to the last shown photo-thumbnail.
1554 after this, unset the last_photo session variable.
1556 if(isset($_SESSION['last_photo'])) {
1557 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1558 unset($_SESSION['last_photo']);
1563 } // showPhotoIndex()
1566 * show credit template
1568 public function showCredits()
1570 $this->tmpl->assign('version', $this->cfg->version);
1571 $this->tmpl->assign('product', $this->cfg->product);
1572 $this->tmpl->assign('db_version', $this->dbver);
1573 $this->tmpl->show("credits.tpl");
1578 * create thumbnails for the requested width
1580 * this function creates image thumbnails of $orig_image
1581 * stored as $thumb_image. It will check if the image is
1582 * in a supported format, if necessary rotate the image
1583 * (based on EXIF orientation meta headers) and re-sizing.
1584 * @param string $orig_image
1585 * @param string $thumb_image
1586 * @param integer $width
1589 public function create_thumbnail($orig_image, $thumb_image, $width)
1591 if(!file_exists($orig_image)) {
1595 $mime = $this->get_mime_info($orig_image);
1597 /* check if original photo is a support image type */
1598 if(!$this->checkifImageSupported($mime))
1605 $meta = $this->get_meta_informations($orig_image);
1611 if(isset($meta['Orientation'])) {
1612 switch($meta['Orientation']) {
1613 case 1: /* top, left */
1614 /* nothing to do */ break;
1615 case 2: /* top, right */
1616 $rotate = 0; $flip_hori = true; break;
1617 case 3: /* bottom, left */
1618 $rotate = 180; break;
1619 case 4: /* bottom, right */
1620 $flip_vert = true; break;
1621 case 5: /* left side, top */
1622 $rotate = 90; $flip_vert = true; break;
1623 case 6: /* right side, top */
1624 $rotate = 90; break;
1625 case 7: /* left side, bottom */
1626 $rotate = 270; $flip_vert = true; break;
1627 case 8: /* right side, bottom */
1628 $rotate = 270; break;
1632 $src_img = @imagecreatefromjpeg($orig_image);
1638 $src_img = @imagecreatefrompng($orig_image);
1642 case 'image/x-portable-pixmap':
1644 $src_img = new Imagick($orig_image);
1645 $handler = "imagick";
1650 if(!isset($src_img) || empty($src_img)) {
1651 print "Can't load image from ". $orig_image ."\n";
1659 /* grabs the height and width */
1660 $cur_width = imagesx($src_img);
1661 $cur_height = imagesy($src_img);
1663 // If requested width is more then the actual image width,
1664 // do not generate a thumbnail, instead safe the original
1665 // as thumbnail but with lower quality. But if the image
1666 // is to heigh too, then we still have to resize it.
1667 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1668 $result = imagejpeg($src_img, $thumb_image, 75);
1669 imagedestroy($src_img);
1676 $cur_width = $src_img->getImageWidth();
1677 $cur_height = $src_img->getImageHeight();
1679 // If requested width is more then the actual image width,
1680 // do not generate a thumbnail, instead safe the original
1681 // as thumbnail but with lower quality. But if the image
1682 // is to heigh too, then we still have to resize it.
1683 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1684 $src_img->setCompressionQuality(75);
1685 $src_img->setImageFormat('jpeg');
1686 $src_img->writeImage($thumb_image);
1688 $src_img->destroy();
1695 // If the image will be rotate because EXIF orientation said so
1696 // 'virtually rotate' the image for further calculations
1697 if($rotate == 90 || $rotate == 270) {
1699 $cur_width = $cur_height;
1703 /* calculates aspect ratio */
1704 $aspect_ratio = $cur_height / $cur_width;
1707 if($aspect_ratio < 1) {
1709 $new_h = abs($new_w * $aspect_ratio);
1711 /* 'virtually' rotate the image and calculate it's ratio */
1712 $tmp_w = $cur_height;
1713 $tmp_h = $cur_width;
1714 /* now get the ratio from the 'rotated' image */
1715 $tmp_ratio = $tmp_h/$tmp_w;
1716 /* now calculate the new dimensions */
1718 $tmp_h = abs($tmp_w * $tmp_ratio);
1720 // now that we know, how high they photo should be, if it
1721 // gets rotated, use this high to scale the image
1723 $new_w = abs($new_h / $aspect_ratio);
1725 // If the image will be rotate because EXIF orientation said so
1726 // now 'virtually rotate' back the image for the image manipulation
1727 if($rotate == 90 || $rotate == 270) {
1738 /* creates new image of that size */
1739 $dst_img = imagecreatetruecolor($new_w, $new_h);
1741 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1743 /* copies resized portion of original image into new image */
1744 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1746 /* needs the image to be flipped horizontal? */
1748 $this->_debug("(FLIP)");
1749 $dst_img = $this->flipImage($dst_img, 'hori');
1751 /* needs the image to be flipped vertical? */
1753 $this->_debug("(FLIP)");
1754 $dst_img = $this->flipImage($dst_img, 'vert');
1758 $this->_debug("(ROTATE)");
1759 $dst_img = $this->rotateImage($dst_img, $rotate);
1762 /* write down new generated file */
1763 $result = imagejpeg($dst_img, $thumb_image, 75);
1765 /* free your mind */
1766 imagedestroy($dst_img);
1767 imagedestroy($src_img);
1769 if($result === false) {
1770 print "Can't write thumbnail ". $thumb_image ."\n";
1780 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1782 /* needs the image to be flipped horizontal? */
1784 $this->_debug("(FLIP)");
1785 $src_img->rotateImage(new ImagickPixel(), 90);
1786 $src_img->flipImage();
1787 $src_img->rotateImage(new ImagickPixel(), -90);
1789 /* needs the image to be flipped vertical? */
1791 $this->_debug("(FLIP)");
1792 $src_img->flipImage();
1796 $this->_debug("(ROTATE)");
1797 $src_img->rotateImage(new ImagickPixel(), $rotate);
1800 $src_img->setCompressionQuality(75);
1801 $src_img->setImageFormat('jpeg');
1803 if(!$src_img->writeImage($thumb_image)) {
1804 print "Can't write thumbnail ". $thumb_image ."\n";
1809 $src_img->destroy();
1816 } // create_thumbnail()
1819 * return all exif meta data from the file
1820 * @param string $file
1823 public function get_meta_informations($file)
1825 return exif_read_data($file);
1827 } // get_meta_informations()
1830 * create phpfspot own sqlite database
1832 * this function creates phpfspots own sqlite database
1833 * if it does not exist yet. this own is used to store
1834 * some necessary informations (md5 sum's, ...).
1836 public function check_config_table()
1838 // if the config table doesn't exist yet, create it
1839 if(!$this->cfg_db->db_check_table_exists("images")) {
1840 $this->cfg_db->db_exec("
1841 CREATE TABLE images (
1842 img_idx int primary key,
1848 } // check_config_table
1851 * Generates a thumbnail from photo idx
1853 * This function will generate JPEG thumbnails from provided F-Spot photo
1856 * 1. Check if all thumbnail generations (width) are already in place and
1858 * 2. Check if the md5sum of the original file has changed
1859 * 3. Generate the thumbnails if needed
1860 * @param integer $idx
1861 * @param integer $force
1862 * @param boolean $overwrite
1864 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1868 $resolutions = Array(
1869 $this->cfg->thumb_width,
1870 $this->cfg->photo_width,
1871 $this->cfg->mini_width,
1875 /* get details from F-Spot's database */
1876 $details = $this->get_photo_details($idx);
1878 /* calculate file MD5 sum */
1879 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1881 if(!file_exists($full_path)) {
1882 $this->_error("File ". $full_path ." does not exist\n");
1886 if(!is_readable($full_path)) {
1887 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1891 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1893 /* If Nikon NEF format, we need to treat it another way */
1894 if(isset($this->cfg->dcraw_bin) &&
1895 file_exists($this->cfg->dcraw_bin) &&
1896 is_executable($this->cfg->dcraw_bin) &&
1897 preg_match('/\.nef$/i', $details['uri'])) {
1899 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1901 /* if PPM file does not exist, let dcraw convert it from NEF */
1902 if(!file_exists($ppm_path)) {
1903 system($this->cfg->dcraw_bin ." -a ". $full_path);
1906 /* for now we handle the PPM instead of the NEF */
1907 $full_path = $ppm_path;
1911 $file_md5 = md5_file($full_path);
1914 foreach($resolutions as $resolution) {
1916 $generate_it = false;
1918 $thumb_sub_path = substr($file_md5, 0, 2);
1919 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1921 /* if thumbnail-subdirectory does not exist yet, create it */
1922 if(!file_exists(dirname($thumb_path))) {
1923 mkdir(dirname($thumb_path), 0755);
1926 /* if the thumbnail file doesn't exist, create it */
1927 if(!file_exists($thumb_path)) {
1928 $generate_it = true;
1930 /* if the file hasn't changed there is no need to regen the thumb */
1931 elseif($file_md5 != $this->getMD5($idx) || $force) {
1932 $generate_it = true;
1935 if($generate_it || $overwrite) {
1937 $this->_debug(" ". $resolution ."px");
1938 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1946 $this->_debug(" already exist");
1949 /* set the new/changed MD5 sum for the current photo */
1951 $this->setMD5($idx, $file_md5);
1954 $this->_debug("\n");
1959 * returns stored md5 sum for a specific photo
1961 * this function queries the phpfspot database for a
1962 * stored MD5 checksum of the specified photo
1963 * @param integer $idx
1964 * @return string|null
1966 public function getMD5($idx)
1968 $result = $this->cfg_db->db_query("
1971 WHERE img_idx='". $idx ."'
1977 $img = $this->cfg_db->db_fetch_object($result);
1978 return $img['img_md5'];
1983 * set MD5 sum for the specific photo
1984 * @param integer $idx
1985 * @param string $md5
1987 private function setMD5($idx, $md5)
1989 $result = $this->cfg_db->db_exec("
1990 REPLACE INTO images (img_idx, img_md5)
1991 VALUES ('". $idx ."', '". $md5 ."')
1997 * store current tag condition
1999 * this function stores the current tag condition
2000 * (AND or OR) in the users session variables
2001 * @param string $mode
2004 public function setTagCondition($mode)
2006 $_SESSION['tag_condition'] = $mode;
2010 } // setTagCondition()
2013 * invoke tag & date search
2015 * this function will return all matching tags and store
2016 * them in the session variable selected_tags. furthermore
2017 * it also handles the date search.
2018 * getPhotoSelection() will then only return the matching
2022 public function startSearch()
2025 if(isset($_POST['date_from']) && $this->isValidDate($_POST['date_from'])) {
2026 $date_from = $_POST['date_from'];
2028 if(isset($_POST['date_to']) && $this->isValidDate($_POST['date_to'])) {
2029 $date_to = $_POST['date_to'];
2032 /* tag-name search */
2033 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2034 $searchfor_tag = $_POST['for_tag'];
2035 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2038 unset($_SESSION['searchfor_tag']);
2041 /* file-name search */
2042 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2043 $_SESSION['searchfor_name'] = $_POST['for_name'];
2046 unset($_SESSION['searchfor_name']);
2050 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2052 $_SESSION['rate_from'] = $_POST['rate_from'];
2054 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2055 $_SESSION['rate_to'] = $_POST['rate_to'];
2059 /* delete any previously set value */
2060 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2065 if(isset($date_from) && !empty($date_from))
2066 $_SESSION['from_date'] = strtotime($date_from ." 00:00:00");
2068 unset($_SESSION['from_date']);
2070 if(isset($date_to) && !empty($date_to))
2071 $_SESSION['to_date'] = strtotime($date_to ." 23:59:59");
2073 unset($_SESSION['to_date']);
2075 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2076 /* new search, reset the current selected tags */
2077 $_SESSION['selected_tags'] = Array();
2078 foreach($this->avail_tags as $tag) {
2079 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2080 array_push($_SESSION['selected_tags'], $tag);
2089 * updates sort order in session variable
2091 * this function is invoked by RPC and will sort the requested
2092 * sort order in the session variable.
2093 * @param string $sort_order
2096 public function updateSortOrder($order)
2098 if(isset($this->sort_orders[$order])) {
2099 $_SESSION['sort_order'] = $order;
2103 return "unkown error";
2105 } // updateSortOrder()
2110 * this function rotates the image according the
2112 * @param string $img
2113 * @param integer $degress
2116 private function rotateImage($img, $degrees)
2118 if(function_exists("imagerotate")) {
2119 $img = imagerotate($img, $degrees, 0);
2121 function imagerotate($src_img, $angle)
2123 $src_x = imagesx($src_img);
2124 $src_y = imagesy($src_img);
2125 if ($angle == 180) {
2129 elseif ($src_x <= $src_y) {
2133 elseif ($src_x >= $src_y) {
2138 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2139 imagealphablending($rotate, false);
2144 for ($y = 0; $y < ($src_y); $y++) {
2145 for ($x = 0; $x < ($src_x); $x++) {
2146 $color = imagecolorat($src_img, $x, $y);
2147 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2153 for ($y = 0; $y < ($src_y); $y++) {
2154 for ($x = 0; $x < ($src_x); $x++) {
2155 $color = imagecolorat($src_img, $x, $y);
2156 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2162 for ($y = 0; $y < ($src_y); $y++) {
2163 for ($x = 0; $x < ($src_x); $x++) {
2164 $color = imagecolorat($src_img, $x, $y);
2165 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2179 $img = imagerotate($img, $degrees);
2188 * returns flipped image
2190 * this function will return an either horizontal or
2191 * vertical flipped truecolor image.
2192 * @param string $image
2193 * @param string $mode
2196 private function flipImage($image, $mode)
2198 $w = imagesx($image);
2199 $h = imagesy($image);
2200 $flipped = imagecreatetruecolor($w, $h);
2204 for ($y = 0; $y < $h; $y++) {
2205 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2209 for ($x = 0; $x < $w; $x++) {
2210 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2220 * return all assigned tags for the specified photo
2221 * @param integer $idx
2224 private function get_photo_tags($idx)
2226 $result = $this->db->db_query("
2229 INNER JOIN photo_tags pt
2231 WHERE pt.photo_id='". $idx ."'
2236 while($row = $this->db->db_fetch_object($result)) {
2237 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2239 $tags[$row['id']] = $row['name'];
2244 } // get_photo_tags()
2247 * create on-the-fly images with text within
2248 * @param string $txt
2249 * @param string $color
2250 * @param integer $space
2251 * @param integer $font
2254 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2256 if (strlen($color) != 6)
2259 $int = hexdec($color);
2260 $h = imagefontheight($font);
2261 $fw = imagefontwidth($font);
2262 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2263 $lines = count($txt);
2264 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2265 $bg = imagecolorallocate($im, 255, 255, 255);
2266 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2269 foreach ($txt as $text) {
2270 $x = (($w - ($fw * strlen($text))) / 2);
2271 imagestring($im, $font, $x, $y, $text, $color);
2272 $y += ($h + $space);
2275 Header("Content-type: image/png");
2278 } // showTextImage()
2281 * check if all requirements are met
2284 private function check_requirements()
2286 if(!function_exists("imagecreatefromjpeg")) {
2287 print "PHP GD library extension is missing<br />\n";
2291 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2292 print "PHP SQLite3 library extension is missing<br />\n";
2296 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2297 ini_set('track_errors', 1);
2298 @include_once 'HTML/AJAX/Server.php';
2299 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2300 print "PEAR HTML_AJAX package is missing<br />\n";
2303 @include_once 'Calendar/Calendar.php';
2304 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2305 print "PEAR Calendar package is missing<br />\n";
2308 @include_once 'Console/Getopt.php';
2309 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2310 print "PEAR Console_Getopt package is missing<br />\n";
2313 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2314 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2315 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2318 ini_restore('track_errors');
2325 } // check_requirements()
2327 private function _debug($text)
2329 if(isset($this->fromcmd)) {
2336 * check if specified MIME type is supported
2337 * @param string $mime
2340 public function checkifImageSupported($mime)
2342 $supported_types = Array(
2345 "image/x-portable-pixmap",
2349 if(in_array($mime, $supported_types))
2354 } // checkifImageSupported()
2358 * @param string $text
2360 public function _error($text)
2362 switch($this->cfg->logging) {
2365 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2366 print $text ."<br />\n";
2372 error_log($text, 3, $his->cfg->log_file);
2376 $this->runtime_error = true;
2381 * get calendar input-text fields
2383 * this function returns a text-field used for the data selection.
2384 * Either it will be filled with the current date or, if available,
2385 * filled with the date user entered previously.
2387 * @param string $mode
2390 private function get_date_text_field($mode)
2392 $date = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2394 $date.= isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2396 $date.= isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2398 $output = "<input type=\"text\" size=\"15\" id=\"date_". $mode ."\" value=\"". $date ."\"";
2399 if(!isset($_SESSION[$mode .'_date']))
2400 $output.= " disabled=\"disabled\"";
2405 } // get_date_text_field()
2408 * output calendar matrix
2409 * @param integer $year
2410 * @param integer $month
2411 * @param integer $day
2413 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2415 if (!isset($year)) $year = date('Y');
2416 if (!isset($month)) $month = date('m');
2417 if (!isset($day)) $day = date('d');
2422 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2423 require_once CALENDAR_ROOT.'Day.php';
2426 $month = new Calendar_Month_Weekdays($year,$month);
2429 $prevStamp = $month->prevMonth(true);
2430 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2431 $nextStamp = $month->nextMonth(true);
2432 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2434 $selectedDays = array (
2435 new Calendar_Day($year,$month,$day),
2436 new Calendar_Day($year,12,25),
2439 // Build the days in the month
2440 $month->build($selectedDays);
2442 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2443 $this->tmpl->assign('prev_month', $prev);
2444 $this->tmpl->assign('next_month', $next);
2446 while ( $day = $month->fetch() ) {
2448 if(!isset($matrix[$rows]))
2449 $matrix[$rows] = Array();
2453 $dayStamp = $day->thisDay(true);
2454 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2456 // isFirst() to find start of week
2457 if ( $day->isFirst() )
2460 if ( $day->isSelected() ) {
2461 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2462 } else if ( $day->isEmpty() ) {
2463 $string.= "<td> </td>\n";
2465 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2468 // isLast() to find end of week
2469 if ( $day->isLast() )
2470 $string.= "</tr>\n";
2472 $matrix[$rows][$cols] = $string;
2482 $this->tmpl->assign('matrix', $matrix);
2483 $this->tmpl->assign('rows', $rows);
2484 $this->tmpl->show("calendar.tpl");
2486 } // get_calendar_matrix()
2489 * output export page
2490 * @param string $mode
2492 public function getExport($mode)
2494 $pictures = $this->getPhotoSelection();
2495 $current_tags = $this->getCurrentTags();
2497 foreach($pictures as $picture) {
2499 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2500 if($current_tags != "") {
2501 $orig_url.= "&tags=". $current_tags;
2503 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2504 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2507 if($this->is_user_friendly_url()) {
2508 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2511 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2517 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2518 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2522 // "[%pictureurl% %thumbnailurl%]"
2523 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2526 case 'MoinMoinList':
2527 // " * [%pictureurl% %thumbnailurl%]"
2528 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2539 public function getRSSFeed()
2541 Header("Content-type: text/xml; charset=utf-8");
2542 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2545 xmlns:media="http://search.yahoo.com/mrss/"
2546 xmlns:dc="http://purl.org/dc/elements/1.1/"
2549 <title>phpfspot</title>
2550 <description>phpfspot RSS feed</description>
2551 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2552 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2553 <generator>phpfspot</generator>
2556 $pictures = $this->getPhotoSelection();
2557 $current_tags = $this->getCurrentTags();
2559 foreach($pictures as $picture) {
2561 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2562 if($current_tags != "") {
2563 $orig_url.= "&tags=". $current_tags;
2565 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2566 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2569 $details = $this->get_photo_details($picture);
2571 if($this->is_user_friendly_url()) {
2572 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2575 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2578 $thumb_html = htmlspecialchars("
2579 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2581 ". $details['description']);
2583 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2585 /* get EXIF information if JPEG */
2586 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2587 $meta = $this->get_meta_informations($orig_path);
2592 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2593 <link><?php print htmlspecialchars($orig_url); ?></link>
2594 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2595 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $details['time']); ?></dc:date.Taken>
2597 <?php print $thumb_html; ?>
2599 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $details['time']); ?></pubDate>
2614 * get all selected tags
2616 * This function will return all selected tags as one string, seperated
2620 private function getCurrentTags()
2623 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2624 foreach($_SESSION['selected_tags'] as $tag)
2625 $current_tags.= $tag .",";
2626 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2628 return $current_tags;
2630 } // getCurrentTags()
2633 * return the current photo
2635 public function getCurrentPhoto()
2637 if(isset($_SESSION['current_photo'])) {
2638 print $_SESSION['current_photo'];
2640 } // getCurrentPhoto()
2643 * tells the client browser what to do
2645 * this function is getting called via AJAX by the
2646 * client browsers. it will tell them what they have
2647 * to do next. This is necessary for directly jumping
2648 * into photo index or single photo view when the are
2649 * requested with specific URLs
2652 public function whatToDo()
2654 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2656 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2657 return "showpi_tags";
2659 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2666 * return the current process-user
2669 private function getuid()
2671 if($uid = posix_getuid()) {
2672 if($user = posix_getpwuid($uid)) {
2673 return $user['name'];
2682 * returns a select-dropdown box to select photo index sort parameters
2683 * @param array $params
2684 * @param smarty $smarty
2687 public function smarty_sort_select_list($params, &$smarty)
2691 foreach($this->sort_orders as $key => $value) {
2692 $output.= "<option value=\"". $key ."\"";
2693 if($key == $_SESSION['sort_order']) {
2694 $output.= " selected=\"selected\"";
2696 $output.= ">". $value ."</option>";
2701 } // smarty_sort_select_list()
2704 * returns the currently selected sort order
2707 private function get_sort_order()
2709 switch($_SESSION['sort_order']) {
2711 return " ORDER BY p.time ASC";
2714 return " ORDER BY p.time DESC";
2717 if($this->dbver < 9) {
2718 return " ORDER BY p.name ASC";
2721 return " ORDER BY basename(p.uri) ASC";
2725 if($this->dbver < 9) {
2726 return " ORDER BY p.name DESC";
2729 return " ORDER BY basename(p.uri) DESC";
2733 return " ORDER BY t.name ASC ,p.time ASC";
2736 return " ORDER BY t.name DESC ,p.time ASC";
2739 return " ORDER BY p.rating ASC, t.name ASC";
2742 return " ORDER BY p.rating DESC, t.name DESC";
2746 } // get_sort_order()
2749 * return the next to be shown slide show image
2751 * this function returns the URL of the next image
2752 * in the slideshow sequence.
2755 public function getNextSlideShowImage()
2757 $all_photos = $this->getPhotoSelection();
2759 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2760 $_SESSION['slideshow_img'] = 0;
2762 $_SESSION['slideshow_img']++;
2764 if($this->is_user_friendly_url()) {
2765 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2768 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2770 } // getNextSlideShowImage()
2773 * return the previous to be shown slide show image
2775 * this function returns the URL of the previous image
2776 * in the slideshow sequence.
2779 public function getPrevSlideShowImage()
2781 $all_photos = $this->getPhotoSelection();
2783 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2784 $_SESSION['slideshow_img'] = 0;
2786 $_SESSION['slideshow_img']--;
2788 if($this->is_user_friendly_url()) {
2789 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2792 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2794 } // getPrevSlideShowImage()
2796 public function resetSlideShow()
2798 if(isset($_SESSION['slideshow_img']))
2799 unset($_SESSION['slideshow_img']);
2801 } // resetSlideShow()
2806 * this function will get all photos from the fspot
2807 * database and randomly return ONE entry
2809 * saddly there is yet no sqlite3 function which returns
2810 * the bulk result in array, so we have to fill up our
2814 public function get_random_photo()
2823 /* if show_tags is set, only return details for photos which
2824 are specified to be shown
2826 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2828 INNER JOIN photo_tags pt
2833 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2836 $result = $this->db->db_query($query_str);
2838 while($row = $this->db->db_fetch_object($result)) {
2839 array_push($all, $row['id']);
2842 return $all[array_rand($all)];
2844 } // get_random_photo()
2847 * get random photo tag photo
2849 * this function will get all photos tagged with the requested
2850 * tag from the fspot database and randomly return ONE entry
2852 * saddly there is yet no sqlite3 function which returns
2853 * the bulk result in array, so we have to fill up our
2857 public function get_random_tag_photo($tagidx)
2864 INNER JOIN photo_tags pt
2868 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2876 pt.tag_id LIKE '". $tagidx ."'
2879 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2882 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2886 $result = $this->db->db_query($query_str);
2888 while($row = $this->db->db_fetch_object($result)) {
2889 array_push($all, $row['id']);
2892 return $all[array_rand($all)];
2894 } // get_random_tag_photo()
2897 * validates provided date
2899 * this function validates if the provided date
2900 * contains a valid date and will return true
2902 * @param string $date_str
2905 public function isValidDate($date_str)
2907 $timestamp = strtotime($date_str);
2909 if(is_numeric($timestamp))
2917 * timestamp to string conversion
2918 * @param integer $timestamp
2921 private function ts2str($timestamp)
2923 if(!empty($timestamp) && is_numeric($timestamp))
2924 return strftime("%Y-%m-%d", $timestamp);
2929 * extract tag-names from $_GET['tags']
2930 * @param string $tags_str
2933 private function extractTags($tags_str)
2935 $not_validated = split(',', $tags_str);
2936 $validated = array();
2938 foreach($not_validated as $tag) {
2939 if(is_numeric($tag))
2940 array_push($validated, $tag);
2948 * returns the full path to a thumbnail
2949 * @param integer $width
2950 * @param integer $photo
2953 public function get_thumb_path($width, $photo)
2955 $md5 = $this->getMD5($photo);
2956 $sub_path = substr($md5, 0, 2);
2957 return $this->cfg->thumb_path
2965 } // get_thumb_path()
2968 * returns server's virtual host name
2971 private function get_server_name()
2973 return $_SERVER['SERVER_NAME'];
2974 } // get_server_name()
2977 * returns type of webprotocol which is currently used
2980 private function get_web_protocol()
2982 if(!isset($_SERVER['HTTPS']))
2986 } // get_web_protocol()
2989 * return url to this phpfspot installation
2992 private function get_phpfspot_url()
2994 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2996 } // get_phpfspot_url()
2999 * returns the number of photos which are tagged with $tag_id
3000 * @param integer $tag_id
3003 public function get_num_photos($tag_id)
3005 if($result = $this->db->db_fetchSingleRow("
3006 SELECT count(*) as number
3009 tag_id LIKE '". $tag_id ."'")) {
3011 return $result['number'];
3017 } // get_num_photos()
3020 * check file exists and is readable
3022 * returns true, if everything is ok, otherwise false
3023 * if $silent is not set, this function will output and
3025 * @param string $file
3026 * @param boolean $silent
3029 private function check_readable($file, $silent = null)
3031 if(!file_exists($file)) {
3033 print "File \"". $file ."\" does not exist.\n";
3037 if(!is_readable($file)) {
3039 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3045 } // check_readable()
3048 * check if all needed indices are present
3050 * this function checks, if some needed indices are already
3051 * present, or if not, create them on the fly. they are
3052 * necessary to speed up some queries like that one look for
3053 * all tags, when show_tags is specified in the configuration.
3055 private function checkDbIndices()
3057 $result = $this->db->db_exec("
3058 CREATE INDEX IF NOT EXISTS
3065 } // checkDbIndices()
3068 * retrive F-Spot database version
3070 * this function will return the F-Spot database version number
3071 * It is stored within the sqlite3 database in the table meta
3072 * @return string|null
3074 public function getFspotDBVersion()
3076 if($result = $this->db->db_fetchSingleRow("
3077 SELECT data as version
3080 name LIKE 'F-Spot Database Version'
3082 return $result['version'];
3086 } // getFspotDBVersion()
3089 * parse the provided URI and will returned the requested chunk
3090 * @param string $uri
3091 * @param string $mode
3094 public function parse_uri($uri, $mode)
3096 if(($components = parse_url($uri)) !== false) {
3100 return basename($components['path']);
3103 return dirname($components['path']);
3106 return $components['path'];
3116 * validate config options
3118 * this function checks if all necessary configuration options are
3119 * specified and set.
3122 private function check_config_options()
3124 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3125 $this->_error("Please set \$page_title in phpfspot_cfg");
3127 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3128 $this->_error("Please set \$base_path in phpfspot_cfg");
3130 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3131 $this->_error("Please set \$web_path in phpfspot_cfg");
3133 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3134 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3136 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3137 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3139 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3140 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3142 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3143 $this->_error("Please set \$db_access in phpfspot_cfg");
3145 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3146 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3148 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3149 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3151 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3152 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3154 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3155 $this->_error("Please set \$photo_width in phpfspot_cfg");
3157 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3158 $this->_error("Please set \$mini_width in phpfspot_cfg");
3160 if(!isset($this->cfg->thumbs_per_page))
3161 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3163 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3164 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3166 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3167 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3169 if(!isset($this->cfg->hide_tags))
3170 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3172 if(!isset($this->cfg->theme_name))
3173 $this->_error("Please set \$theme_name in phpfspot_cfg");
3175 if(!isset($this->cfg->logging))
3176 $this->_error("Please set \$logging in phpfspot_cfg");
3178 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3180 if(!isset($this->cfg->log_file))
3181 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3183 if(!is_writeable($this->cfg->log_file))
3184 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3188 /* remove trailing slash, if set */
3189 if($this->cfg->web_path == "/")
3190 $this->cfg->web_path = "";
3191 elseif(preg_match('/\/$/', $this->cfg->web_path))
3192 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3194 return $this->runtime_error;
3196 } // check_config_options()
3199 * cleanup phpfspot own database
3201 * When photos are getting delete from F-Spot, there will remain
3202 * remain some residues in phpfspot own database. This function
3203 * will try to wipe them out.
3205 public function cleanup_phpfspot_db()
3207 $to_delete = Array();
3209 $result = $this->cfg_db->db_query("
3212 ORDER BY img_idx ASC
3215 while($row = $this->cfg_db->db_fetch_object($result)) {
3216 if(!$this->db->db_fetchSingleRow("
3219 WHERE id='". $row['img_idx'] ."'")) {
3221 array_push($to_delete, $row['img_idx'], ',');
3225 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3227 $this->cfg_db->db_exec("
3229 WHERE img_idx IN (". implode($to_delete) .")
3232 } // cleanup_phpfspot_db()
3235 * return first image of the page, the $current photo
3238 * this function is used to find out the first photo of the
3239 * current page, in which the $current photo lies. this is
3240 * used to display the correct photo, when calling showPhotoIndex()
3242 * @param integer $current
3243 * @param integer $max
3246 private function getCurrentPage($current, $max)
3248 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3249 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3250 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3256 } // getCurrentPage()
3261 * this function tries to find out the correct mime-type
3262 * for the provided file.
3263 * @param string $file
3266 public function get_mime_info($file)
3268 $details = getimagesize($file);
3270 /* if getimagesize() returns empty, try at least to find out the
3273 if(empty($details) && function_exists('mime_content_type')) {
3275 // mime_content_type is marked as deprecated in the documentation,
3276 // but is it really necessary to force users to install a PECL
3278 $details['mime'] = mime_content_type($file);
3281 return $details['mime'];
3283 } // get_mime_info()
3286 * return tag-name by tag-idx
3288 * this function returns the tag-name for the requested
3289 * tag specified by tag-idx.
3290 * @param integer $idx
3293 public function get_tag_name($idx)
3295 if($result = $this->db->db_fetchSingleRow("
3299 id LIKE '". $idx ."'")) {
3301 return $result['name'];
3310 * parse user friendly url which got rewritten by the websever
3311 * @param string $request_uri
3314 private function parse_user_friendly_url($request_uri)
3316 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3318 $options = explode('/', $request_uri);
3320 switch($options[1]) {
3322 if(is_numeric($options[2])) {
3323 $this->session_cleanup();
3324 //unset($_SESSION['start_action']);
3325 //unset($_SESSION['selected_tags']);
3326 $_GET['mode'] = 'showp';
3327 return $this->showPhoto($options[2]);
3331 if(is_numeric($options[2])) {
3332 require_once "phpfspot_img.php";
3333 $img = new PHPFSPOT_IMG;
3334 if(isset($options[3]) && is_numeric($options[3]))
3335 $img->showImg($options[2], $options[3]);
3337 $img->showImg($options[2]);
3342 if(is_numeric($options[2])) {
3343 $this->session_cleanup();
3344 $_GET['tags'] = $options[2];
3345 $_SESSION['selected_tags'] = Array($options[2]);
3346 if(isset($options[3]) && is_numeric($options[3]))
3347 $_SESSION['begin_with'] = $options[3];
3348 return $this->showPhotoIndex();
3354 } // parse_user_friendly_url()
3357 * check if user-friendly-urls are enabled
3359 * this function will return true, if the config option
3360 * $user_friendly_url has been set. Otherwise false.
3363 private function is_user_friendly_url()
3365 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3370 } // is_user_friendly_url()
3375 * this function will cleanup user's session information
3377 private function session_cleanup()
3379 unset($_SESSION['begin_with']);
3380 $this->resetDateSearch();
3381 $this->resetPhotoView();
3382 $this->resetTagSearch();
3383 $this->resetNameSearch();
3384 $this->resetDateSearch();
3387 } // session_cleanup()