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(!isset($_SESSION['sort_order']))
231 $_SESSION['sort_order'] = 'date_desc';
233 if(!isset($_SESSION['searchfor_tag']))
234 $_SESSION['searchfor_tag'] = '';
236 // if begin_with is still set but thumbs_per_page is now 0, unset it
237 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
238 unset($_SESSION['begin_with']);
240 // if user-friendly-url's are enabled, set also a flag for the template handler
241 if($this->is_user_friendly_url()) {
242 $this->tmpl->assign('user_friendly_url', 'true');
247 public function __destruct()
253 * show - generate html output
255 * this function can be called after the constructor has
256 * prepared everyhing. it will load the index.tpl smarty
257 * template. if necessary it will registere pre-selects
258 * (photo index, photo, tag search, date search) into
261 public function show()
263 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
264 $this->tmpl->assign('page_title', $this->cfg->page_title);
265 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
266 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
269 if($this->is_user_friendly_url()) {
270 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
273 if(isset($_GET['mode'])) {
275 $_SESSION['start_action'] = $_GET['mode'];
277 switch($_GET['mode']) {
279 if(isset($_GET['tags'])) {
280 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
282 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
283 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
285 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
286 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
290 if(isset($_GET['tags'])) {
291 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
292 $_SESSION['start_action'] = 'showp';
294 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
295 $_SESSION['current_photo'] = $_GET['id'];
296 $_SESSION['start_action'] = 'showp';
298 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
299 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
301 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
302 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
306 /* fetch export template */
307 print $this->tmpl->fetch("export.tpl");
308 /* no further execution necessary. */
312 /* fetch slideshow template */
313 print $this->tmpl->show("slideshow.tpl");
314 /* no further execution necessary. */
318 if(isset($_GET['tags'])) {
319 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
321 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
322 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
324 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
325 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
333 /* if date-search variables are registered in the session, set the check
334 for "consider date-range" in the html output
336 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
337 $this->tmpl->assign('date_search_enabled', true);
339 /* if rate-search variables are registered in the session, set the check
340 for "consider rate-range" in the html output
342 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
343 $this->tmpl->assign('rate_search_enabled', true);
346 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
347 $this->tmpl->assign('search_from_date', $this->get_date_text_field('from'));
348 $this->tmpl->assign('search_to_date', $this->get_date_text_field('to'));
350 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
351 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
352 $this->tmpl->assign('rate_search', $this->get_rate_search());
354 /* if no site-content has been set yet... */
355 if(!isset($content)) {
356 /* if tags are already selected, we can immediately display photo-index */
357 if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) &&
358 isset($_SESSION['start_action']) && $_SESSION['start_action'] != 'showp') ||
359 (isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi'))
360 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
362 /* if a photo is already selected, we can immediately display single-photo */
363 if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo']))
364 $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo']));
366 /* ok, then let us show the welcome page... */
367 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
372 $this->tmpl->assign('initial_content', $content);
374 $this->tmpl->show("index.tpl");
379 * get_tags - grab all tags of f-spot's database
381 * this function will get all available tags from
382 * the f-spot database and store them within two
383 * arrays within this class for later usage. in
384 * fact, if the user requests (hide_tags) it will
385 * opt-out some of them.
387 * this function is getting called once by show()
389 private function get_tags()
391 $this->avail_tags = Array();
394 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
397 DISTINCT t1.id as id, t1.name as name
400 INNER JOIN photo_tags
401 pt2 ON pt1.photo_id=pt2.photo_id
407 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
409 t1.sort_priority ASC";
411 $result = $this->db->db_query($query_str);
415 $result = $this->db->db_query("
418 ORDER BY sort_priority ASC
422 while($row = $this->db->db_fetch_object($result)) {
424 $tag_id = $row['id'];
425 $tag_name = $row['name'];
427 /* if the user has specified to ignore this tag in phpfspot's
428 configuration, ignore it here so it does not get added to
431 if(in_array($row['name'], $this->cfg->hide_tags))
434 /* if you include the following if-clause and the user has specified
435 to only show certain tags which are specified in phpfspot's
436 configuration, ignore all others so they will not be added to the
438 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
439 !in_array($row['name'], $this->cfg->show_tags))
443 $this->tags[$tag_id] = $tag_name;
444 $this->avail_tags[$count] = $tag_id;
452 * extract all photo details
454 * retrieve all available details from f-spot's
455 * database and return them as object
456 * @param integer $idx
457 * @return object|null
459 public function get_photo_details($idx)
461 if($this->dbver < 9) {
463 SELECT p.id, p.name, p.time, p.directory_path, p.description
468 /* till F-Spot version 0.4.1 */
469 if($this->dbver < 11) {
471 SELECT p.id, p.uri, p.time, p.description
477 SELECT p.id, p.uri, p.time, p.description, p.rating
483 /* if show_tags is set, only return details for photos which
484 are specified to be shown
486 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
488 INNER JOIN photo_tags pt
492 WHERE p.id='". $idx ."'
493 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
497 WHERE p.id='". $idx ."'
501 if($result = $this->db->db_query($query_str)) {
503 $row = $this->db->db_fetch_object($result);
505 if($this->dbver < 9) {
506 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
515 } // get_photo_details
518 * returns aligned photo names
520 * this function returns aligned (length) names for
521 * an specific photo. If the length of the name exceeds
522 * $limit the name will be shrinked (...)
523 * @param integer $idx
524 * @param integer $limit
525 * @return string|null
527 public function getPhotoName($idx, $limit = 0)
529 if($details = $this->get_photo_details($idx)) {
530 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
531 $name = $this->shrink_text($long_name, $limit);
541 * get photo rating level
543 * this function will return the integer-based rating
544 * level of the photo. This can only be done, if the F-Spot
545 * database is at a specific level. If rating value can not
546 * be found, zero will be returned indicating no rating value
551 public function get_photo_rating($idx)
553 if($detail = $this->get_photo_details($idx)) {
554 if(isset($detail['rating']))
555 return $detail['rating'];
560 } // get_photo_rating()
563 * get rate-search bars
565 * this function will return the rating-bars for the
569 public function get_rate_search()
573 for($i = 1; $i <= 5; $i++) {
575 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
577 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
578 $bar.= $this->cfg->web_path ."/resources/star.png";
580 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
583 onmouseover=\"show_rate('from', ". $i .");\"
584 onmouseout=\"reset_rate('from');\"
585 onclick=\"set_rate('from', ". $i .")\" />";
590 for($i = 1; $i <= 5; $i++) {
592 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
594 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
595 $bar.= $this->cfg->web_path ."/resources/star.png";
597 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
600 onmouseover=\"show_rate('to', ". $i .");\"
601 onmouseout=\"reset_rate('to');\"
602 onclick=\"set_rate('to', ". $i .");\" />";
607 } // get_rate_search()
610 * shrink text according provided limit
612 * If the length of the name exceeds $limit the
613 * text will be shortend and some content in between
614 * will be replaced with "..."
616 * @param integer $limit
619 private function shrink_text($text, $limit)
621 if($limit != 0 && strlen($text) > $limit) {
622 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
630 * translate f-spoth photo path
632 * as the full-qualified path recorded in the f-spot database
633 * is usally not the same as on the webserver, this function
634 * will replace the path with that one specified in the cfg
635 * @param string $path
638 public function translate_path($path)
640 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
645 * control HTML ouput for a single photo
647 * this function provides all the necessary information
648 * for the single photo template.
649 * @param integer photo
651 public function showPhoto($photo)
653 /* get all photos from the current photo selection */
654 $all_photos = $this->getPhotoSelection();
655 $count = count($all_photos);
657 for($i = 0; $i < $count; $i++) {
659 // $get_next will be set, when the photo which has to
660 // be displayed has been found - this means that the
661 // next available is in fact the NEXT image (for the
663 if(isset($get_next)) {
664 $next_img = $all_photos[$i];
668 /* the next photo is our NEXT photo */
669 if($all_photos[$i] == $photo) {
673 $previous_img = $all_photos[$i];
676 if($photo == $all_photos[$i]) {
681 $details = $this->get_photo_details($photo);
688 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
689 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
691 if(!file_exists($orig_path)) {
692 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
696 if(!is_readable($orig_path)) {
697 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
701 /* If the thumbnail doesn't exist yet, try to create it */
702 if(!file_exists($thumb_path)) {
703 $this->gen_thumb($photo, true);
704 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
707 /* get mime-type, height and width from the original photo */
708 $info = getimagesize($orig_path);
710 /* get EXIF information if JPEG */
711 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
712 $meta = $this->get_meta_informations($orig_path);
715 /* If EXIF data are available, use them */
716 if(isset($meta['ExifImageWidth'])) {
717 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
719 $meta_res = $info[0] ."x". $info[1];
722 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
723 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
725 $extern_link = "index.php?mode=showp&id=". $photo;
726 $current_tags = $this->getCurrentTags();
727 if($current_tags != "") {
728 $extern_link.= "&tags=". $current_tags;
730 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
731 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
734 $this->tmpl->assign('extern_link', $extern_link);
736 if(!file_exists($thumb_path)) {
737 $this->_error("Can't open file ". $thumb_path ."\n");
741 $info_thumb = getimagesize($thumb_path);
743 $this->tmpl->assign('description', $details['description']);
744 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
745 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
747 $this->tmpl->assign('width', $info_thumb[0]);
748 $this->tmpl->assign('height', $info_thumb[1]);
749 $this->tmpl->assign('ExifMadeOn', strftime("%a %x %X", $details['time']));
750 $this->tmpl->assign('ExifMadeWith', $meta_make);
751 $this->tmpl->assign('ExifOrigResolution', $meta_res);
752 $this->tmpl->assign('ExifFileSize', $meta_size);
754 if($this->is_user_friendly_url()) {
755 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
756 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
759 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
760 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
763 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
765 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
766 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
767 $this->tmpl->assign('current_img', $photo);
769 if(isset($previous_img)) {
770 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
771 $this->tmpl->assign('prev_img', $previous_img);
774 if(isset($next_img)) {
775 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
776 $this->tmpl->assign('next_img', $next_img);
779 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
780 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
781 $this->tmpl->assign('photo_number', $i);
782 $this->tmpl->assign('photo_count', count($all_photos));
784 return $this->tmpl->fetch("single_photo.tpl");
789 * all available tags and tag cloud
791 * this function outputs all available tags (time ordered)
792 * and in addition output them as tag cloud (tags which have
793 * many photos will appears more then others)
795 public function getAvailableTags()
797 /* retrive tags from database */
802 $result = $this->db->db_query("
803 SELECT tag_id as id, count(tag_id) as quantity
813 while($row = $this->db->db_fetch_object($result)) {
814 $tags[$row['id']] = $row['quantity'];
817 // change these font sizes if you will
818 $max_size = 125; // max font size in %
819 $min_size = 75; // min font size in %
822 $max_sat = hexdec('cc');
823 $min_sat = hexdec('44');
825 // get the largest and smallest array values
826 $max_qty = max(array_values($tags));
827 $min_qty = min(array_values($tags));
829 // find the range of values
830 $spread = $max_qty - $min_qty;
831 if (0 == $spread) { // we don't want to divide by zero
835 // determine the font-size increment
836 // this is the increase per tag quantity (times used)
837 $step = ($max_size - $min_size)/($spread);
838 $step_sat = ($max_sat - $min_sat)/($spread);
840 // loop through our tag array
841 foreach ($tags as $key => $value) {
843 /* has the currently processed tag already been added to
844 the selected tag list? if so, ignore it here...
846 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
849 // calculate CSS font-size
850 // find the $value in excess of $min_qty
851 // multiply by the font-size increment ($size)
852 // and add the $min_size set above
853 $size = $min_size + (($value - $min_qty) * $step);
854 // uncomment if you want sizes in whole %:
857 $color = $min_sat + ($value - $min_qty) * $step_sat;
863 if(isset($this->tags[$key])) {
864 if($this->is_user_friendly_url()) {
865 $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\"
866 onclick=\"Tags('add', ". $key ."); return false;\"
868 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
869 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
872 $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\"
873 onclick=\"Tags('add', ". $key ."); return false;\"
875 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
876 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
881 $output = substr($output, 0, strlen($output)-2);
884 } // getAvailableTags()
887 * output all selected tags
889 * this function output all tags which have been selected
890 * by the user. the selected tags are stored in the
891 * session-variable $_SESSION['selected_tags']
894 public function getSelectedTags($type = 'link')
896 /* retrive tags from database */
901 foreach($this->avail_tags as $tag)
903 // return all selected tags
904 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
909 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
913 <div class=\"tagresulttag\">
914 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
915 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
925 $output = substr($output, 0, strlen($output)-2);
929 return "no tags selected";
932 } // getSelectedTags()
935 * add tag to users session variable
937 * this function will add the specified to users current
938 * tag selection. if a date search has been made before
939 * it will be now cleared
942 public function addTag($tag)
944 if(!isset($_SESSION['selected_tags']))
945 $_SESSION['selected_tags'] = Array();
947 if(isset($_SESSION['searchfor_tag']))
948 unset($_SESSION['searchfor_tag']);
950 // has the user requested to hide this tag, and still someone,
951 // somehow tries to add it, don't allow this.
952 if(!isset($this->cfg->hide_tags) &&
953 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
956 if(!in_array($tag, $_SESSION['selected_tags']))
957 array_push($_SESSION['selected_tags'], $tag);
964 * remove tag to users session variable
966 * this function removes the specified tag from
967 * users current tag selection
971 public function delTag($tag)
973 if(isset($_SESSION['searchfor_tag']))
974 unset($_SESSION['searchfor_tag']);
976 if(isset($_SESSION['selected_tags'])) {
977 $key = array_search($tag, $_SESSION['selected_tags']);
978 unset($_SESSION['selected_tags'][$key]);
979 sort($_SESSION['selected_tags']);
987 * reset tag selection
989 * if there is any tag selection, it will be
992 public function resetTags()
994 if(isset($_SESSION['selected_tags']))
995 unset($_SESSION['selected_tags']);
1000 * returns the value for the autocomplete tag-search
1003 public function get_xml_tag_list()
1005 if(!isset($_GET['search']) || !is_string($_GET['search']))
1006 $_GET['search'] = '';
1011 /* retrive tags from database */
1014 $matched_tags = Array();
1016 header("Content-Type: text/xml");
1018 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1019 $string.= "<results>\n";
1021 foreach($this->avail_tags as $tag)
1023 if(!empty($_GET['search']) &&
1024 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1025 count($matched_tags) < $length) {
1027 $count = $this->get_num_photos($tag);
1030 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1033 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1039 /* if we have collected enough items, break out */
1040 if(count($matched_tags) >= $length)
1044 $string.= "</results>\n";
1048 } // get_xml_tag_list()
1052 * reset single photo
1054 * if a specific photo was requested (external link)
1055 * unset the session variable now
1057 public function resetPhotoView()
1059 if(isset($_SESSION['current_photo']))
1060 unset($_SESSION['current_photo']);
1062 } // resetPhotoView();
1067 * if any tag search has taken place, reset it now
1069 public function resetTagSearch()
1071 if(isset($_SESSION['searchfor_tag']))
1072 unset($_SESSION['searchfor_tag']);
1074 } // resetTagSearch()
1079 * if any name search has taken place, reset it now
1081 public function resetNameSearch()
1083 if(isset($_SESSION['searchfor_name']))
1084 unset($_SESSION['searchfor_name']);
1086 } // resetNameSearch()
1091 * if any date search has taken place, reset it now.
1093 public function resetDateSearch()
1095 if(isset($_SESSION['from_date']))
1096 unset($_SESSION['from_date']);
1097 if(isset($_SESSION['to_date']))
1098 unset($_SESSION['to_date']);
1100 } // resetDateSearch();
1105 * if any rate search has taken place, reset it now.
1107 public function resetRateSearch()
1109 if(isset($_SESSION['rate_from']))
1110 unset($_SESSION['rate_from']);
1111 if(isset($_SESSION['rate_to']))
1112 unset($_SESSION['rate_to']);
1114 } // resetRateSearch();
1117 * return all photo according selection
1119 * this function returns all photos based on
1120 * the tag-selection, tag- or date-search.
1121 * the tag-search also has to take care of AND
1122 * and OR conjunctions
1125 public function getPhotoSelection()
1127 $matched_photos = Array();
1128 $additional_where_cond = "";
1130 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1131 $from_date = $_SESSION['from_date'];
1132 $to_date = $_SESSION['to_date'];
1133 $additional_where_cond.= "
1134 p.time>='". $from_date ."'
1136 p.time<='". $to_date ."'
1140 if(isset($_SESSION['searchfor_name'])) {
1142 /* check for previous conditions. if so add 'AND' */
1143 if(!empty($additional_where_cond)) {
1144 $additional_where_cond.= " AND ";
1147 if($this->dbver < 9) {
1148 $additional_where_cond.= "
1150 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1152 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1157 $additional_where_cond.= "
1159 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1161 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1167 /* limit result based on rate-search */
1168 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1170 if($this->dbver > 10) {
1172 /* check for previous conditions. if so add 'AND' */
1173 if(!empty($additional_where_cond)) {
1174 $additional_where_cond.= " AND ";
1177 $additional_where_cond.= "
1178 p.rating >= ". $_SESSION['rate_from'] ."
1180 p.rating <= ". $_SESSION['rate_to'] ."
1185 if(isset($_SESSION['sort_order'])) {
1186 $order_str = $this->get_sort_order();
1189 /* return a search result */
1190 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1192 SELECT DISTINCT pt1.photo_id
1194 INNER JOIN photo_tags pt2
1195 ON pt1.photo_id=pt2.photo_id
1199 ON pt1.photo_id=p.id
1202 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1204 if(!empty($additional_where_cond))
1205 $query_str.= "AND ". $additional_where_cond ." ";
1207 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1208 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1211 if(isset($order_str))
1212 $query_str.= $order_str;
1214 $result = $this->db->db_query($query_str);
1215 while($row = $this->db->db_fetch_object($result)) {
1216 array_push($matched_photos, $row['photo_id']);
1218 return $matched_photos;
1221 /* return according the selected tags */
1222 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1224 foreach($_SESSION['selected_tags'] as $tag)
1225 $selected.= $tag .",";
1226 $selected = substr($selected, 0, strlen($selected)-1);
1228 /* photo has to match at least on of the selected tags */
1229 if($_SESSION['tag_condition'] == 'or') {
1231 SELECT DISTINCT pt1.photo_id
1233 INNER JOIN photo_tags pt2
1234 ON pt1.photo_id=pt2.photo_id
1238 ON pt1.photo_id=p.id
1239 WHERE pt1.tag_id IN (". $selected .")
1241 if(!empty($additional_where_cond))
1242 $query_str.= "AND ". $additional_where_cond ." ";
1244 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1245 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1248 if(isset($order_str))
1249 $query_str.= $order_str;
1251 /* photo has to match all selected tags */
1252 elseif($_SESSION['tag_condition'] == 'and') {
1254 if(count($_SESSION['selected_tags']) >= 32) {
1255 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1256 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1260 /* Join together a table looking like
1262 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1264 so the query can quickly return all images matching the
1265 selected tags in an AND condition
1270 SELECT DISTINCT pt1.photo_id
1274 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1281 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1283 INNER JOIN photo_tags pt". ($i+2) ."
1284 ON pt1.photo_id=pt". ($i+2) .".photo_id
1289 ON pt1.photo_id=p.id
1291 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1292 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1294 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1297 if(!empty($additional_where_cond))
1298 $query_str.= "AND ". $additional_where_cond;
1300 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1301 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1304 if(isset($order_str))
1305 $query_str.= $order_str;
1309 $result = $this->db->db_query($query_str);
1310 while($row = $this->db->db_fetch_object($result)) {
1311 array_push($matched_photos, $row['photo_id']);
1313 return $matched_photos;
1316 /* return all available photos */
1318 SELECT DISTINCT p.id
1320 LEFT JOIN photo_tags pt
1326 if(!empty($additional_where_cond))
1327 $query_str.= "WHERE ". $additional_where_cond ." ";
1329 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1330 if(!empty($additional_where_cond))
1331 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1333 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1336 if(isset($order_str))
1337 $query_str.= $order_str;
1339 $result = $this->db->db_query($query_str);
1340 while($row = $this->db->db_fetch_object($result)) {
1341 array_push($matched_photos, $row['id']);
1343 return $matched_photos;
1345 } // getPhotoSelection()
1348 * control HTML ouput for photo index
1350 * this function provides all the necessary information
1351 * for the photo index template.
1354 public function showPhotoIndex()
1356 $photos = $this->getPhotoSelection();
1357 $current_tags = $this->getCurrentTags();
1359 $count = count($photos);
1361 /* if all thumbnails should be shown on one page */
1362 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1366 /* thumbnails should be splitted up in several pages */
1367 elseif($this->cfg->thumbs_per_page > 0) {
1369 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1373 $begin_with = $_SESSION['begin_with'];
1376 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1380 $images[$thumbs] = Array();
1381 $img_height[$thumbs] = Array();
1382 $img_width[$thumbs] = Array();
1383 $img_id[$thumbs] = Array();
1384 $img_name[$thumbs] = Array();
1385 $img_fullname[$thumbs] = Array();
1386 $img_title = Array();
1387 $img_rating = Array();
1389 for($i = $begin_with; $i < $end_with; $i++) {
1391 if(isset($photos[$i])) {
1393 $images[$thumbs] = $photos[$i];
1394 $img_id[$thumbs] = $i;
1395 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1396 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1397 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1398 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1400 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1402 if(file_exists($thumb_path)) {
1403 $info = getimagesize($thumb_path);
1404 $img_width[$thumbs] = $info[0];
1405 $img_height[$thumbs] = $info[1];
1411 // +1 for for smarty's selection iteration
1414 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1415 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1417 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1418 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1419 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1422 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1423 $this->tmpl->assign('tag_result', 1);
1426 /* do we have to display the page selector ? */
1427 if($this->cfg->thumbs_per_page != 0) {
1431 /* calculate the page switchers */
1432 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1433 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1435 if($begin_with != 0)
1436 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1437 if($end_with < $count)
1438 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1440 $photo_per_page = $this->cfg->thumbs_per_page;
1441 $last_page = ceil($count / $photo_per_page);
1443 /* get the current selected page */
1444 if($begin_with == 0) {
1448 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1455 for($i = 1; $i <= $last_page; $i++) {
1457 if($current_page == $i)
1458 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1459 elseif($current_page-1 == $i || $current_page+1 == $i)
1460 $style = "style=\"font-size: 105%;\"";
1461 elseif(($current_page-5 >= $i) && ($i != 1) ||
1462 ($current_page+5 <= $i) && ($i != $last_page))
1463 $style = "style=\"font-size: 75%;\"";
1467 $start_with = ($i*$photo_per_page)-$photo_per_page;
1469 if($this->is_user_friendly_url()) {
1470 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1473 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1475 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1479 $select.= ">". $i ."</a> ";
1481 // until 9 pages we show the selector from 1-9
1482 if($last_page <= 9) {
1483 $page_select.= $select;
1486 if($i == 1 /* first page */ ||
1487 $i == $last_page /* last page */ ||
1488 $i == $current_page /* current page */ ||
1489 $i == ceil($last_page * 0.25) /* first quater */ ||
1490 $i == ceil($last_page * 0.5) /* half */ ||
1491 $i == ceil($last_page * 0.75) /* third quater */ ||
1492 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1493 (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 */ ||
1494 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1495 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1497 $page_select.= $select;
1505 $page_select.= "......... ";
1510 /* only show the page selector if we have more then one page */
1512 $this->tmpl->assign('page_selector', $page_select);
1515 $extern_link = "index.php?mode=showpi";
1516 $rss_link = "index.php?mode=rss";
1517 if($current_tags != "") {
1518 $extern_link.= "&tags=". $current_tags;
1519 $rss_link.= "&tags=". $current_tags;
1521 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1522 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1523 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1526 $export_link = "index.php?mode=export";
1527 $slideshow_link = "index.php?mode=slideshow";
1529 $this->tmpl->assign('extern_link', $extern_link);
1530 $this->tmpl->assign('slideshow_link', $slideshow_link);
1531 $this->tmpl->assign('export_link', $export_link);
1532 $this->tmpl->assign('rss_link', $rss_link);
1533 $this->tmpl->assign('count', $count);
1534 $this->tmpl->assign('width', $this->cfg->thumb_width);
1535 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1536 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1537 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1538 $this->tmpl->assign('images', $images);
1539 $this->tmpl->assign('img_width', $img_width);
1540 $this->tmpl->assign('img_height', $img_height);
1541 $this->tmpl->assign('img_id', $img_id);
1542 $this->tmpl->assign('img_name', $img_name);
1543 $this->tmpl->assign('img_fullname', $img_fullname);
1544 $this->tmpl->assign('img_title', $img_title);
1545 $this->tmpl->assign('img_rating', $img_rating);
1546 $this->tmpl->assign('thumbs', $thumbs);
1547 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1549 $result = $this->tmpl->fetch("photo_index.tpl");
1551 /* if we are returning to photo index from an photo-view,
1552 scroll the window to the last shown photo-thumbnail.
1553 after this, unset the last_photo session variable.
1555 if(isset($_SESSION['last_photo'])) {
1556 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1557 unset($_SESSION['last_photo']);
1562 } // showPhotoIndex()
1565 * show credit template
1567 public function showCredits()
1569 $this->tmpl->assign('version', $this->cfg->version);
1570 $this->tmpl->assign('product', $this->cfg->product);
1571 $this->tmpl->assign('db_version', $this->dbver);
1572 $this->tmpl->show("credits.tpl");
1577 * create thumbnails for the requested width
1579 * this function creates image thumbnails of $orig_image
1580 * stored as $thumb_image. It will check if the image is
1581 * in a supported format, if necessary rotate the image
1582 * (based on EXIF orientation meta headers) and re-sizing.
1583 * @param string $orig_image
1584 * @param string $thumb_image
1585 * @param integer $width
1588 public function create_thumbnail($orig_image, $thumb_image, $width)
1590 if(!file_exists($orig_image)) {
1594 $mime = $this->get_mime_info($orig_image);
1596 /* check if original photo is a support image type */
1597 if(!$this->checkifImageSupported($mime))
1604 $meta = $this->get_meta_informations($orig_image);
1610 if(isset($meta['Orientation'])) {
1611 switch($meta['Orientation']) {
1612 case 1: /* top, left */
1613 /* nothing to do */ break;
1614 case 2: /* top, right */
1615 $rotate = 0; $flip_hori = true; break;
1616 case 3: /* bottom, left */
1617 $rotate = 180; break;
1618 case 4: /* bottom, right */
1619 $flip_vert = true; break;
1620 case 5: /* left side, top */
1621 $rotate = 90; $flip_vert = true; break;
1622 case 6: /* right side, top */
1623 $rotate = 90; break;
1624 case 7: /* left side, bottom */
1625 $rotate = 270; $flip_vert = true; break;
1626 case 8: /* right side, bottom */
1627 $rotate = 270; break;
1631 $src_img = @imagecreatefromjpeg($orig_image);
1637 $src_img = @imagecreatefrompng($orig_image);
1641 case 'image/x-portable-pixmap':
1643 $src_img = new Imagick($orig_image);
1644 $handler = "imagick";
1649 if(!isset($src_img) || empty($src_img)) {
1650 print "Can't load image from ". $orig_image ."\n";
1658 /* grabs the height and width */
1659 $cur_width = imagesx($src_img);
1660 $cur_height = imagesy($src_img);
1662 // If requested width is more then the actual image width,
1663 // do not generate a thumbnail, instead safe the original
1664 // as thumbnail but with lower quality. But if the image
1665 // is to heigh too, then we still have to resize it.
1666 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1667 $result = imagejpeg($src_img, $thumb_image, 75);
1668 imagedestroy($src_img);
1675 $cur_width = $src_img->getImageWidth();
1676 $cur_height = $src_img->getImageHeight();
1678 // If requested width is more then the actual image width,
1679 // do not generate a thumbnail, instead safe the original
1680 // as thumbnail but with lower quality. But if the image
1681 // is to heigh too, then we still have to resize it.
1682 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1683 $src_img->setCompressionQuality(75);
1684 $src_img->setImageFormat('jpeg');
1685 $src_img->writeImage($thumb_image);
1687 $src_img->destroy();
1694 // If the image will be rotate because EXIF orientation said so
1695 // 'virtually rotate' the image for further calculations
1696 if($rotate == 90 || $rotate == 270) {
1698 $cur_width = $cur_height;
1702 /* calculates aspect ratio */
1703 $aspect_ratio = $cur_height / $cur_width;
1706 if($aspect_ratio < 1) {
1708 $new_h = abs($new_w * $aspect_ratio);
1710 /* 'virtually' rotate the image and calculate it's ratio */
1711 $tmp_w = $cur_height;
1712 $tmp_h = $cur_width;
1713 /* now get the ratio from the 'rotated' image */
1714 $tmp_ratio = $tmp_h/$tmp_w;
1715 /* now calculate the new dimensions */
1717 $tmp_h = abs($tmp_w * $tmp_ratio);
1719 // now that we know, how high they photo should be, if it
1720 // gets rotated, use this high to scale the image
1722 $new_w = abs($new_h / $aspect_ratio);
1724 // If the image will be rotate because EXIF orientation said so
1725 // now 'virtually rotate' back the image for the image manipulation
1726 if($rotate == 90 || $rotate == 270) {
1737 /* creates new image of that size */
1738 $dst_img = imagecreatetruecolor($new_w, $new_h);
1740 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1742 /* copies resized portion of original image into new image */
1743 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1745 /* needs the image to be flipped horizontal? */
1747 $this->_debug("(FLIP)");
1748 $dst_img = $this->flipImage($dst_img, 'hori');
1750 /* needs the image to be flipped vertical? */
1752 $this->_debug("(FLIP)");
1753 $dst_img = $this->flipImage($dst_img, 'vert');
1757 $this->_debug("(ROTATE)");
1758 $dst_img = $this->rotateImage($dst_img, $rotate);
1761 /* write down new generated file */
1762 $result = imagejpeg($dst_img, $thumb_image, 75);
1764 /* free your mind */
1765 imagedestroy($dst_img);
1766 imagedestroy($src_img);
1768 if($result === false) {
1769 print "Can't write thumbnail ". $thumb_image ."\n";
1779 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1781 /* needs the image to be flipped horizontal? */
1783 $this->_debug("(FLIP)");
1784 $src_img->rotateImage(new ImagickPixel(), 90);
1785 $src_img->flipImage();
1786 $src_img->rotateImage(new ImagickPixel(), -90);
1788 /* needs the image to be flipped vertical? */
1790 $this->_debug("(FLIP)");
1791 $src_img->flipImage();
1795 $this->_debug("(ROTATE)");
1796 $src_img->rotateImage(new ImagickPixel(), $rotate);
1799 $src_img->setCompressionQuality(75);
1800 $src_img->setImageFormat('jpeg');
1802 if(!$src_img->writeImage($thumb_image)) {
1803 print "Can't write thumbnail ". $thumb_image ."\n";
1808 $src_img->destroy();
1815 } // create_thumbnail()
1818 * return all exif meta data from the file
1819 * @param string $file
1822 public function get_meta_informations($file)
1824 return exif_read_data($file);
1826 } // get_meta_informations()
1829 * create phpfspot own sqlite database
1831 * this function creates phpfspots own sqlite database
1832 * if it does not exist yet. this own is used to store
1833 * some necessary informations (md5 sum's, ...).
1835 public function check_config_table()
1837 // if the config table doesn't exist yet, create it
1838 if(!$this->cfg_db->db_check_table_exists("images")) {
1839 $this->cfg_db->db_exec("
1840 CREATE TABLE images (
1841 img_idx int primary key,
1847 } // check_config_table
1850 * Generates a thumbnail from photo idx
1852 * This function will generate JPEG thumbnails from provided F-Spot photo
1855 * 1. Check if all thumbnail generations (width) are already in place and
1857 * 2. Check if the md5sum of the original file has changed
1858 * 3. Generate the thumbnails if needed
1859 * @param integer $idx
1860 * @param integer $force
1861 * @param boolean $overwrite
1863 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1867 $resolutions = Array(
1868 $this->cfg->thumb_width,
1869 $this->cfg->photo_width,
1870 $this->cfg->mini_width,
1874 /* get details from F-Spot's database */
1875 $details = $this->get_photo_details($idx);
1877 /* calculate file MD5 sum */
1878 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1880 if(!file_exists($full_path)) {
1881 $this->_error("File ". $full_path ." does not exist\n");
1885 if(!is_readable($full_path)) {
1886 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1890 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1892 /* If Nikon NEF format, we need to treat it another way */
1893 if(isset($this->cfg->dcraw_bin) &&
1894 file_exists($this->cfg->dcraw_bin) &&
1895 is_executable($this->cfg->dcraw_bin) &&
1896 preg_match('/\.nef$/i', $details['uri'])) {
1898 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1900 /* if PPM file does not exist, let dcraw convert it from NEF */
1901 if(!file_exists($ppm_path)) {
1902 system($this->cfg->dcraw_bin ." -a ". $full_path);
1905 /* for now we handle the PPM instead of the NEF */
1906 $full_path = $ppm_path;
1910 $file_md5 = md5_file($full_path);
1913 foreach($resolutions as $resolution) {
1915 $generate_it = false;
1917 $thumb_sub_path = substr($file_md5, 0, 2);
1918 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1920 /* if thumbnail-subdirectory does not exist yet, create it */
1921 if(!file_exists(dirname($thumb_path))) {
1922 mkdir(dirname($thumb_path), 0755);
1925 /* if the thumbnail file doesn't exist, create it */
1926 if(!file_exists($thumb_path)) {
1927 $generate_it = true;
1929 /* if the file hasn't changed there is no need to regen the thumb */
1930 elseif($file_md5 != $this->getMD5($idx) || $force) {
1931 $generate_it = true;
1934 if($generate_it || $overwrite) {
1936 $this->_debug(" ". $resolution ."px");
1937 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1945 $this->_debug(" already exist");
1948 /* set the new/changed MD5 sum for the current photo */
1950 $this->setMD5($idx, $file_md5);
1953 $this->_debug("\n");
1958 * returns stored md5 sum for a specific photo
1960 * this function queries the phpfspot database for a
1961 * stored MD5 checksum of the specified photo
1962 * @param integer $idx
1963 * @return string|null
1965 public function getMD5($idx)
1967 $result = $this->cfg_db->db_query("
1970 WHERE img_idx='". $idx ."'
1976 $img = $this->cfg_db->db_fetch_object($result);
1977 return $img['img_md5'];
1982 * set MD5 sum for the specific photo
1983 * @param integer $idx
1984 * @param string $md5
1986 private function setMD5($idx, $md5)
1988 $result = $this->cfg_db->db_exec("
1989 REPLACE INTO images (img_idx, img_md5)
1990 VALUES ('". $idx ."', '". $md5 ."')
1996 * store current tag condition
1998 * this function stores the current tag condition
1999 * (AND or OR) in the users session variables
2000 * @param string $mode
2003 public function setTagCondition($mode)
2005 $_SESSION['tag_condition'] = $mode;
2009 } // setTagCondition()
2012 * invoke tag & date search
2014 * this function will return all matching tags and store
2015 * them in the session variable selected_tags. furthermore
2016 * it also handles the date search.
2017 * getPhotoSelection() will then only return the matching
2021 public function startSearch()
2024 if(isset($_POST['date_from']) && $this->isValidDate($_POST['date_from'])) {
2025 $date_from = $_POST['date_from'];
2027 if(isset($_POST['date_to']) && $this->isValidDate($_POST['date_to'])) {
2028 $date_to = $_POST['date_to'];
2031 /* tag-name search */
2032 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2033 $searchfor_tag = $_POST['for_tag'];
2034 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2037 unset($_SESSION['searchfor_tag']);
2040 /* file-name search */
2041 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2042 $_SESSION['searchfor_name'] = $_POST['for_name'];
2045 unset($_SESSION['searchfor_name']);
2049 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2051 $_SESSION['rate_from'] = $_POST['rate_from'];
2053 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2054 $_SESSION['rate_to'] = $_POST['rate_to'];
2058 /* delete any previously set value */
2059 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2064 if(isset($date_from) && !empty($date_from))
2065 $_SESSION['from_date'] = strtotime($date_from ." 00:00:00");
2067 unset($_SESSION['from_date']);
2069 if(isset($date_to) && !empty($date_to))
2070 $_SESSION['to_date'] = strtotime($date_to ." 23:59:59");
2072 unset($_SESSION['to_date']);
2074 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2075 /* new search, reset the current selected tags */
2076 $_SESSION['selected_tags'] = Array();
2077 foreach($this->avail_tags as $tag) {
2078 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2079 array_push($_SESSION['selected_tags'], $tag);
2088 * updates sort order in session variable
2090 * this function is invoked by RPC and will sort the requested
2091 * sort order in the session variable.
2092 * @param string $sort_order
2095 public function updateSortOrder($order)
2097 if(isset($this->sort_orders[$order])) {
2098 $_SESSION['sort_order'] = $order;
2102 return "unkown error";
2104 } // updateSortOrder()
2109 * this function rotates the image according the
2111 * @param string $img
2112 * @param integer $degress
2115 private function rotateImage($img, $degrees)
2117 if(function_exists("imagerotate")) {
2118 $img = imagerotate($img, $degrees, 0);
2120 function imagerotate($src_img, $angle)
2122 $src_x = imagesx($src_img);
2123 $src_y = imagesy($src_img);
2124 if ($angle == 180) {
2128 elseif ($src_x <= $src_y) {
2132 elseif ($src_x >= $src_y) {
2137 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2138 imagealphablending($rotate, false);
2143 for ($y = 0; $y < ($src_y); $y++) {
2144 for ($x = 0; $x < ($src_x); $x++) {
2145 $color = imagecolorat($src_img, $x, $y);
2146 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2152 for ($y = 0; $y < ($src_y); $y++) {
2153 for ($x = 0; $x < ($src_x); $x++) {
2154 $color = imagecolorat($src_img, $x, $y);
2155 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2161 for ($y = 0; $y < ($src_y); $y++) {
2162 for ($x = 0; $x < ($src_x); $x++) {
2163 $color = imagecolorat($src_img, $x, $y);
2164 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2178 $img = imagerotate($img, $degrees);
2187 * returns flipped image
2189 * this function will return an either horizontal or
2190 * vertical flipped truecolor image.
2191 * @param string $image
2192 * @param string $mode
2195 private function flipImage($image, $mode)
2197 $w = imagesx($image);
2198 $h = imagesy($image);
2199 $flipped = imagecreatetruecolor($w, $h);
2203 for ($y = 0; $y < $h; $y++) {
2204 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2208 for ($x = 0; $x < $w; $x++) {
2209 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2219 * return all assigned tags for the specified photo
2220 * @param integer $idx
2223 private function get_photo_tags($idx)
2225 $result = $this->db->db_query("
2228 INNER JOIN photo_tags pt
2230 WHERE pt.photo_id='". $idx ."'
2235 while($row = $this->db->db_fetch_object($result)) {
2236 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2238 $tags[$row['id']] = $row['name'];
2243 } // get_photo_tags()
2246 * create on-the-fly images with text within
2247 * @param string $txt
2248 * @param string $color
2249 * @param integer $space
2250 * @param integer $font
2253 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2255 if (strlen($color) != 6)
2258 $int = hexdec($color);
2259 $h = imagefontheight($font);
2260 $fw = imagefontwidth($font);
2261 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2262 $lines = count($txt);
2263 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2264 $bg = imagecolorallocate($im, 255, 255, 255);
2265 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2268 foreach ($txt as $text) {
2269 $x = (($w - ($fw * strlen($text))) / 2);
2270 imagestring($im, $font, $x, $y, $text, $color);
2271 $y += ($h + $space);
2274 Header("Content-type: image/png");
2277 } // showTextImage()
2280 * check if all requirements are met
2283 private function check_requirements()
2285 if(!function_exists("imagecreatefromjpeg")) {
2286 print "PHP GD library extension is missing<br />\n";
2290 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2291 print "PHP SQLite3 library extension is missing<br />\n";
2295 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2296 ini_set('track_errors', 1);
2297 @include_once 'HTML/AJAX/Server.php';
2298 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2299 print "PEAR HTML_AJAX package is missing<br />\n";
2302 @include_once 'Calendar/Calendar.php';
2303 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2304 print "PEAR Calendar package is missing<br />\n";
2307 @include_once 'Console/Getopt.php';
2308 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2309 print "PEAR Console_Getopt package is missing<br />\n";
2312 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2313 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2314 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2317 ini_restore('track_errors');
2324 } // check_requirements()
2326 private function _debug($text)
2328 if(isset($this->fromcmd)) {
2335 * check if specified MIME type is supported
2336 * @param string $mime
2339 public function checkifImageSupported($mime)
2341 $supported_types = Array(
2344 "image/x-portable-pixmap",
2348 if(in_array($mime, $supported_types))
2353 } // checkifImageSupported()
2357 * @param string $text
2359 public function _error($text)
2361 switch($this->cfg->logging) {
2364 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2365 print $text ."<br />\n";
2371 error_log($text, 3, $his->cfg->log_file);
2375 $this->runtime_error = true;
2380 * get calendar input-text fields
2382 * this function returns a text-field used for the data selection.
2383 * Either it will be filled with the current date or, if available,
2384 * filled with the date user entered previously.
2386 * @param string $mode
2389 private function get_date_text_field($mode)
2391 $date = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2393 $date.= isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2395 $date.= isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2397 $output = "<input type=\"text\" size=\"15\" id=\"date_". $mode ."\" value=\"". $date ."\"";
2398 if(!isset($_SESSION[$mode .'_date']))
2399 $output.= " disabled=\"disabled\"";
2404 } // get_date_text_field()
2407 * output calendar matrix
2408 * @param integer $year
2409 * @param integer $month
2410 * @param integer $day
2412 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2414 if (!isset($year)) $year = date('Y');
2415 if (!isset($month)) $month = date('m');
2416 if (!isset($day)) $day = date('d');
2421 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2422 require_once CALENDAR_ROOT.'Day.php';
2425 $month = new Calendar_Month_Weekdays($year,$month);
2428 $prevStamp = $month->prevMonth(true);
2429 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2430 $nextStamp = $month->nextMonth(true);
2431 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2433 $selectedDays = array (
2434 new Calendar_Day($year,$month,$day),
2435 new Calendar_Day($year,12,25),
2438 // Build the days in the month
2439 $month->build($selectedDays);
2441 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2442 $this->tmpl->assign('prev_month', $prev);
2443 $this->tmpl->assign('next_month', $next);
2445 while ( $day = $month->fetch() ) {
2447 if(!isset($matrix[$rows]))
2448 $matrix[$rows] = Array();
2452 $dayStamp = $day->thisDay(true);
2453 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2455 // isFirst() to find start of week
2456 if ( $day->isFirst() )
2459 if ( $day->isSelected() ) {
2460 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2461 } else if ( $day->isEmpty() ) {
2462 $string.= "<td> </td>\n";
2464 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2467 // isLast() to find end of week
2468 if ( $day->isLast() )
2469 $string.= "</tr>\n";
2471 $matrix[$rows][$cols] = $string;
2481 $this->tmpl->assign('matrix', $matrix);
2482 $this->tmpl->assign('rows', $rows);
2483 $this->tmpl->show("calendar.tpl");
2485 } // get_calendar_matrix()
2488 * output export page
2489 * @param string $mode
2491 public function getExport($mode)
2493 $pictures = $this->getPhotoSelection();
2494 $current_tags = $this->getCurrentTags();
2496 foreach($pictures as $picture) {
2498 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2499 if($current_tags != "") {
2500 $orig_url.= "&tags=". $current_tags;
2502 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2503 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2506 if($this->is_user_friendly_url()) {
2507 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2510 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2516 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2517 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2521 // "[%pictureurl% %thumbnailurl%]"
2522 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2525 case 'MoinMoinList':
2526 // " * [%pictureurl% %thumbnailurl%]"
2527 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2538 public function getRSSFeed()
2540 Header("Content-type: text/xml; charset=utf-8");
2541 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2544 xmlns:media="http://search.yahoo.com/mrss/"
2545 xmlns:dc="http://purl.org/dc/elements/1.1/"
2548 <title>phpfspot</title>
2549 <description>phpfspot RSS feed</description>
2550 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2551 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2552 <generator>phpfspot</generator>
2555 $pictures = $this->getPhotoSelection();
2556 $current_tags = $this->getCurrentTags();
2558 foreach($pictures as $picture) {
2560 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2561 if($current_tags != "") {
2562 $orig_url.= "&tags=". $current_tags;
2564 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2565 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2568 $details = $this->get_photo_details($picture);
2570 if($this->is_user_friendly_url()) {
2571 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2574 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2577 $thumb_html = htmlspecialchars("
2578 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2580 ". $details['description']);
2582 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2584 /* get EXIF information if JPEG */
2585 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2586 $meta = $this->get_meta_informations($orig_path);
2591 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2592 <link><?php print htmlspecialchars($orig_url); ?></link>
2593 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2594 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $details['time']); ?></dc:date.Taken>
2596 <?php print $thumb_html; ?>
2598 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $details['time']); ?></pubDate>
2613 * get all selected tags
2615 * This function will return all selected tags as one string, seperated
2619 private function getCurrentTags()
2622 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2623 foreach($_SESSION['selected_tags'] as $tag)
2624 $current_tags.= $tag .",";
2625 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2627 return $current_tags;
2629 } // getCurrentTags()
2632 * return the current photo
2634 public function getCurrentPhoto()
2636 if(isset($_SESSION['current_photo'])) {
2637 print $_SESSION['current_photo'];
2639 } // getCurrentPhoto()
2642 * tells the client browser what to do
2644 * this function is getting called via AJAX by the
2645 * client browsers. it will tell them what they have
2646 * to do next. This is necessary for directly jumping
2647 * into photo index or single photo view when the are
2648 * requested with specific URLs
2651 public function whatToDo()
2653 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2655 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2656 return "showpi_tags";
2658 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2665 * return the current process-user
2668 private function getuid()
2670 if($uid = posix_getuid()) {
2671 if($user = posix_getpwuid($uid)) {
2672 return $user['name'];
2681 * returns a select-dropdown box to select photo index sort parameters
2682 * @param array $params
2683 * @param smarty $smarty
2686 public function smarty_sort_select_list($params, &$smarty)
2690 foreach($this->sort_orders as $key => $value) {
2691 $output.= "<option value=\"". $key ."\"";
2692 if($key == $_SESSION['sort_order']) {
2693 $output.= " selected=\"selected\"";
2695 $output.= ">". $value ."</option>";
2700 } // smarty_sort_select_list()
2703 * returns the currently selected sort order
2706 private function get_sort_order()
2708 switch($_SESSION['sort_order']) {
2710 return " ORDER BY p.time ASC";
2713 return " ORDER BY p.time DESC";
2716 if($this->dbver < 9) {
2717 return " ORDER BY p.name ASC";
2720 return " ORDER BY basename(p.uri) ASC";
2724 if($this->dbver < 9) {
2725 return " ORDER BY p.name DESC";
2728 return " ORDER BY basename(p.uri) DESC";
2732 return " ORDER BY t.name ASC ,p.time ASC";
2735 return " ORDER BY t.name DESC ,p.time ASC";
2738 return " ORDER BY p.rating ASC, t.name ASC";
2741 return " ORDER BY p.rating DESC, t.name DESC";
2745 } // get_sort_order()
2748 * return the next to be shown slide show image
2750 * this function returns the URL of the next image
2751 * in the slideshow sequence.
2754 public function getNextSlideShowImage()
2756 $all_photos = $this->getPhotoSelection();
2758 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2759 $_SESSION['slideshow_img'] = 0;
2761 $_SESSION['slideshow_img']++;
2763 if($this->is_user_friendly_url()) {
2764 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2767 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2769 } // getNextSlideShowImage()
2772 * return the previous to be shown slide show image
2774 * this function returns the URL of the previous image
2775 * in the slideshow sequence.
2778 public function getPrevSlideShowImage()
2780 $all_photos = $this->getPhotoSelection();
2782 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2783 $_SESSION['slideshow_img'] = 0;
2785 $_SESSION['slideshow_img']--;
2787 if($this->is_user_friendly_url()) {
2788 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2791 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2793 } // getPrevSlideShowImage()
2795 public function resetSlideShow()
2797 if(isset($_SESSION['slideshow_img']))
2798 unset($_SESSION['slideshow_img']);
2800 } // resetSlideShow()
2805 * this function will get all photos from the fspot
2806 * database and randomly return ONE entry
2808 * saddly there is yet no sqlite3 function which returns
2809 * the bulk result in array, so we have to fill up our
2813 public function get_random_photo()
2822 /* if show_tags is set, only return details for photos which
2823 are specified to be shown
2825 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2827 INNER JOIN photo_tags pt
2832 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2835 $result = $this->db->db_query($query_str);
2837 while($row = $this->db->db_fetch_object($result)) {
2838 array_push($all, $row['id']);
2841 return $all[array_rand($all)];
2843 } // get_random_photo()
2846 * get random photo tag photo
2848 * this function will get all photos tagged with the requested
2849 * tag from the fspot database and randomly return ONE entry
2851 * saddly there is yet no sqlite3 function which returns
2852 * the bulk result in array, so we have to fill up our
2856 public function get_random_tag_photo($tagidx)
2863 INNER JOIN photo_tags pt
2867 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2875 pt.tag_id LIKE '". $tagidx ."'
2878 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2881 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2885 $result = $this->db->db_query($query_str);
2887 while($row = $this->db->db_fetch_object($result)) {
2888 array_push($all, $row['id']);
2891 return $all[array_rand($all)];
2893 } // get_random_tag_photo()
2896 * validates provided date
2898 * this function validates if the provided date
2899 * contains a valid date and will return true
2901 * @param string $date_str
2904 public function isValidDate($date_str)
2906 $timestamp = strtotime($date_str);
2908 if(is_numeric($timestamp))
2916 * timestamp to string conversion
2917 * @param integer $timestamp
2920 private function ts2str($timestamp)
2922 if(!empty($timestamp) && is_numeric($timestamp))
2923 return strftime("%Y-%m-%d", $timestamp);
2928 * extract tag-names from $_GET['tags']
2929 * @param string $tags_str
2932 private function extractTags($tags_str)
2934 $not_validated = split(',', $tags_str);
2935 $validated = array();
2937 foreach($not_validated as $tag) {
2938 if(is_numeric($tag))
2939 array_push($validated, $tag);
2947 * returns the full path to a thumbnail
2948 * @param integer $width
2949 * @param integer $photo
2952 public function get_thumb_path($width, $photo)
2954 $md5 = $this->getMD5($photo);
2955 $sub_path = substr($md5, 0, 2);
2956 return $this->cfg->thumb_path
2964 } // get_thumb_path()
2967 * returns server's virtual host name
2970 private function get_server_name()
2972 return $_SERVER['SERVER_NAME'];
2973 } // get_server_name()
2976 * returns type of webprotocol which is currently used
2979 private function get_web_protocol()
2981 if(!isset($_SERVER['HTTPS']))
2985 } // get_web_protocol()
2988 * return url to this phpfspot installation
2991 private function get_phpfspot_url()
2993 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2995 } // get_phpfspot_url()
2998 * returns the number of photos which are tagged with $tag_id
2999 * @param integer $tag_id
3002 public function get_num_photos($tag_id)
3004 if($result = $this->db->db_fetchSingleRow("
3005 SELECT count(*) as number
3008 tag_id LIKE '". $tag_id ."'")) {
3010 return $result['number'];
3016 } // get_num_photos()
3019 * check file exists and is readable
3021 * returns true, if everything is ok, otherwise false
3022 * if $silent is not set, this function will output and
3024 * @param string $file
3025 * @param boolean $silent
3028 private function check_readable($file, $silent = null)
3030 if(!file_exists($file)) {
3032 print "File \"". $file ."\" does not exist.\n";
3036 if(!is_readable($file)) {
3038 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3044 } // check_readable()
3047 * check if all needed indices are present
3049 * this function checks, if some needed indices are already
3050 * present, or if not, create them on the fly. they are
3051 * necessary to speed up some queries like that one look for
3052 * all tags, when show_tags is specified in the configuration.
3054 private function checkDbIndices()
3056 $result = $this->db->db_exec("
3057 CREATE INDEX IF NOT EXISTS
3064 } // checkDbIndices()
3067 * retrive F-Spot database version
3069 * this function will return the F-Spot database version number
3070 * It is stored within the sqlite3 database in the table meta
3071 * @return string|null
3073 public function getFspotDBVersion()
3075 if($result = $this->db->db_fetchSingleRow("
3076 SELECT data as version
3079 name LIKE 'F-Spot Database Version'
3081 return $result['version'];
3085 } // getFspotDBVersion()
3088 * parse the provided URI and will returned the requested chunk
3089 * @param string $uri
3090 * @param string $mode
3093 public function parse_uri($uri, $mode)
3095 if(($components = parse_url($uri)) !== false) {
3099 return basename($components['path']);
3102 return dirname($components['path']);
3105 return $components['path'];
3115 * validate config options
3117 * this function checks if all necessary configuration options are
3118 * specified and set.
3121 private function check_config_options()
3123 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3124 $this->_error("Please set \$page_title in phpfspot_cfg");
3126 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3127 $this->_error("Please set \$base_path in phpfspot_cfg");
3129 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3130 $this->_error("Please set \$web_path in phpfspot_cfg");
3132 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3133 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3135 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3136 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3138 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3139 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3141 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3142 $this->_error("Please set \$db_access in phpfspot_cfg");
3144 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3145 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3147 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3148 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3150 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3151 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3153 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3154 $this->_error("Please set \$photo_width in phpfspot_cfg");
3156 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3157 $this->_error("Please set \$mini_width in phpfspot_cfg");
3159 if(!isset($this->cfg->thumbs_per_page))
3160 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3162 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3163 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3165 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3166 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3168 if(!isset($this->cfg->hide_tags))
3169 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3171 if(!isset($this->cfg->theme_name))
3172 $this->_error("Please set \$theme_name in phpfspot_cfg");
3174 if(!isset($this->cfg->logging))
3175 $this->_error("Please set \$logging in phpfspot_cfg");
3177 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3179 if(!isset($this->cfg->log_file))
3180 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3182 if(!is_writeable($this->cfg->log_file))
3183 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3187 /* remove trailing slash, if set */
3188 if($this->cfg->web_path == "/")
3189 $this->cfg->web_path = "";
3190 elseif(preg_match('/\/$/', $this->cfg->web_path))
3191 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3193 return $this->runtime_error;
3195 } // check_config_options()
3198 * cleanup phpfspot own database
3200 * When photos are getting delete from F-Spot, there will remain
3201 * remain some residues in phpfspot own database. This function
3202 * will try to wipe them out.
3204 public function cleanup_phpfspot_db()
3206 $to_delete = Array();
3208 $result = $this->cfg_db->db_query("
3211 ORDER BY img_idx ASC
3214 while($row = $this->cfg_db->db_fetch_object($result)) {
3215 if(!$this->db->db_fetchSingleRow("
3218 WHERE id='". $row['img_idx'] ."'")) {
3220 array_push($to_delete, $row['img_idx'], ',');
3224 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3226 $this->cfg_db->db_exec("
3228 WHERE img_idx IN (". implode($to_delete) .")
3231 } // cleanup_phpfspot_db()
3234 * return first image of the page, the $current photo
3237 * this function is used to find out the first photo of the
3238 * current page, in which the $current photo lies. this is
3239 * used to display the correct photo, when calling showPhotoIndex()
3241 * @param integer $current
3242 * @param integer $max
3245 private function getCurrentPage($current, $max)
3247 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3248 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3249 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3255 } // getCurrentPage()
3260 * this function tries to find out the correct mime-type
3261 * for the provided file.
3262 * @param string $file
3265 public function get_mime_info($file)
3267 $details = getimagesize($file);
3269 /* if getimagesize() returns empty, try at least to find out the
3272 if(empty($details) && function_exists('mime_content_type')) {
3274 // mime_content_type is marked as deprecated in the documentation,
3275 // but is it really necessary to force users to install a PECL
3277 $details['mime'] = mime_content_type($file);
3280 return $details['mime'];
3282 } // get_mime_info()
3285 * return tag-name by tag-idx
3287 * this function returns the tag-name for the requested
3288 * tag specified by tag-idx.
3289 * @param integer $idx
3292 public function get_tag_name($idx)
3294 if($result = $this->db->db_fetchSingleRow("
3298 id LIKE '". $idx ."'")) {
3300 return $result['name'];
3309 * parse user friendly url which got rewritten by the websever
3310 * @param string $request_uri
3313 private function parse_user_friendly_url($request_uri)
3315 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3317 $options = explode('/', $request_uri);
3319 switch($options[1]) {
3321 if(is_numeric($options[2])) {
3322 $this->session_cleanup();
3323 //unset($_SESSION['start_action']);
3324 //unset($_SESSION['selected_tags']);
3325 $_GET['mode'] = 'showp';
3326 return $this->showPhoto($options[2]);
3330 if(is_numeric($options[2])) {
3331 require_once "phpfspot_img.php";
3332 $img = new PHPFSPOT_IMG;
3333 if(isset($options[3]) && is_numeric($options[3]))
3334 $img->showImg($options[2], $options[3]);
3336 $img->showImg($options[2]);
3341 if(is_numeric($options[2])) {
3342 $this->session_cleanup();
3343 $_GET['tags'] = $options[2];
3344 $_SESSION['selected_tags'] = Array($options[2]);
3345 if(isset($options[3]) && is_numeric($options[3]))
3346 $_SESSION['begin_with'] = $options[3];
3347 return $this->showPhotoIndex();
3353 } // parse_user_friendly_url()
3356 * check if user-friendly-urls are enabled
3358 * this function will return true, if the config option
3359 * $user_friendly_url has been set. Otherwise false.
3362 private function is_user_friendly_url()
3364 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3369 } // is_user_friendly_url()
3374 * this function will cleanup user's session information
3376 private function session_cleanup()
3378 unset($_SESSION['begin_with']);
3379 $this->resetDateSearch();
3380 $this->resetPhotoView();
3381 $this->resetTagSearch();
3382 $this->resetNameSearch();
3383 $this->resetDateSearch();
3386 } // session_cleanup()