3 /***************************************************************************
5 * phpfspot, presents your F-Spot photo collection in Web browsers.
7 * Copyright (c) by Andreas Unterkircher
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 ***************************************************************************/
25 require_once "phpfspot_cfg.php";
26 require_once "phpfspot_db.php";
31 * this class contains the most functions which will to the major
39 * phpfspot configuration
47 * SQLite database handle to f-spot database
55 * SQLite database handle to phpfspot database
63 * Smarty template engine
64 * @link http://smarty.php.net smarty.php.net
65 * @see PHPFSPOT_TMPL()
79 * list of available, not-selected, tags
86 * true if runtime error occued
90 private $runtime_error = false;
93 * F-Spot database version
100 * class constructor ($cfg, $db, $cfg_db, $tmpl, $db_ver)
102 * this function will be called on class construct
103 * and will check requirements, loads configuration,
104 * open databases and start the user session
106 public function __construct()
109 * register PHPFSPOT class global
111 * @global PHPFSPOT $GLOBALS['phpfspot']
114 $GLOBALS['phpfspot'] =& $this;
116 $this->cfg = new PHPFSPOT_CFG;
118 /* verify config settings */
119 if($this->check_config_options()) {
123 /* set application name and version information */
124 $this->cfg->product = "phpfspot";
125 $this->cfg->version = "1.5";
127 $this->sort_orders= array(
128 'date_asc' => 'Date ↑',
129 'date_desc' => 'Date ↓',
130 'name_asc' => 'Name ↑',
131 'name_desc' => 'Name ↓',
132 'tags_asc' => 'Tags ↑',
133 'tags_desc' => 'Tags ↓',
136 /* Check necessary requirements */
137 if(!$this->check_requirements()) {
141 /******* Opening F-Spot's sqlite database *********/
143 /* Check if database file is writeable */
144 if(!is_writeable($this->cfg->fspot_db)) {
145 print "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 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
846 // calculate CSS font-size
847 // find the $value in excess of $min_qty
848 // multiply by the font-size increment ($size)
849 // and add the $min_size set above
850 $size = $min_size + (($value - $min_qty) * $step);
851 // uncomment if you want sizes in whole %:
854 $color = $min_sat + ($value - $min_qty) * $step_sat;
860 if(isset($this->tags[$key])) {
861 if($this->is_user_friendly_url())
862 $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
864 $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
868 $output = substr($output, 0, strlen($output)-2);
871 } // getAvailableTags()
874 * output all selected tags
876 * this function output all tags which have been selected
877 * by the user. the selected tags are stored in the
878 * session-variable $_SESSION['selected_tags']
881 public function getSelectedTags($type = 'link')
883 /* retrive tags from database */
888 foreach($this->avail_tags as $tag)
890 // return all selected tags
891 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
896 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
900 <div class=\"tagresulttag\">
901 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
902 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
912 $output = substr($output, 0, strlen($output)-2);
916 return "no tags selected";
919 } // getSelectedTags()
922 * add tag to users session variable
924 * this function will add the specified to users current
925 * tag selection. if a date search has been made before
926 * it will be now cleared
929 public function addTag($tag)
931 if(!isset($_SESSION['selected_tags']))
932 $_SESSION['selected_tags'] = Array();
934 if(isset($_SESSION['searchfor_tag']))
935 unset($_SESSION['searchfor_tag']);
937 // has the user requested to hide this tag, and still someone,
938 // somehow tries to add it, don't allow this.
939 if(!isset($this->cfg->hide_tags) &&
940 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
943 if(!in_array($tag, $_SESSION['selected_tags']))
944 array_push($_SESSION['selected_tags'], $tag);
951 * remove tag to users session variable
953 * this function removes the specified tag from
954 * users current tag selection
958 public function delTag($tag)
960 if(isset($_SESSION['searchfor_tag']))
961 unset($_SESSION['searchfor_tag']);
963 if(isset($_SESSION['selected_tags'])) {
964 $key = array_search($tag, $_SESSION['selected_tags']);
965 unset($_SESSION['selected_tags'][$key]);
966 sort($_SESSION['selected_tags']);
974 * reset tag selection
976 * if there is any tag selection, it will be
979 public function resetTags()
981 if(isset($_SESSION['selected_tags']))
982 unset($_SESSION['selected_tags']);
987 * returns the value for the autocomplete tag-search
990 public function get_xml_tag_list()
992 if(!isset($_GET['search']) || !is_string($_GET['search']))
993 $_GET['search'] = '';
998 /* retrive tags from database */
1001 $matched_tags = Array();
1003 header("Content-Type: text/xml");
1005 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1006 $string.= "<results>\n";
1008 foreach($this->avail_tags as $tag)
1010 if(!empty($_GET['search']) &&
1011 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1012 count($matched_tags) < $length) {
1014 $count = $this->get_num_photos($tag);
1017 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1020 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1026 /* if we have collected enough items, break out */
1027 if(count($matched_tags) >= $length)
1031 $string.= "</results>\n";
1035 } // get_xml_tag_list()
1039 * reset single photo
1041 * if a specific photo was requested (external link)
1042 * unset the session variable now
1044 public function resetPhotoView()
1046 if(isset($_SESSION['current_photo']))
1047 unset($_SESSION['current_photo']);
1049 } // resetPhotoView();
1054 * if any tag search has taken place, reset it now
1056 public function resetTagSearch()
1058 if(isset($_SESSION['searchfor_tag']))
1059 unset($_SESSION['searchfor_tag']);
1061 } // resetTagSearch()
1066 * if any name search has taken place, reset it now
1068 public function resetNameSearch()
1070 if(isset($_SESSION['searchfor_name']))
1071 unset($_SESSION['searchfor_name']);
1073 } // resetNameSearch()
1078 * if any date search has taken place, reset it now.
1080 public function resetDateSearch()
1082 if(isset($_SESSION['from_date']))
1083 unset($_SESSION['from_date']);
1084 if(isset($_SESSION['to_date']))
1085 unset($_SESSION['to_date']);
1087 } // resetDateSearch();
1092 * if any rate search has taken place, reset it now.
1094 public function resetRateSearch()
1096 if(isset($_SESSION['rate_from']))
1097 unset($_SESSION['rate_from']);
1098 if(isset($_SESSION['rate_to']))
1099 unset($_SESSION['rate_to']);
1101 } // resetRateSearch();
1104 * return all photo according selection
1106 * this function returns all photos based on
1107 * the tag-selection, tag- or date-search.
1108 * the tag-search also has to take care of AND
1109 * and OR conjunctions
1112 public function getPhotoSelection()
1114 $matched_photos = Array();
1115 $additional_where_cond = "";
1117 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1118 $from_date = $_SESSION['from_date'];
1119 $to_date = $_SESSION['to_date'];
1120 $additional_where_cond.= "
1121 p.time>='". $from_date ."'
1123 p.time<='". $to_date ."'
1127 if(isset($_SESSION['searchfor_name'])) {
1129 /* check for previous conditions. if so add 'AND' */
1130 if(!empty($additional_where_cond)) {
1131 $additional_where_cond.= " AND ";
1134 if($this->dbver < 9) {
1135 $additional_where_cond.= "
1137 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1139 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1144 $additional_where_cond.= "
1146 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1148 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1154 /* limit result based on rate-search */
1155 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1157 if($this->dbver > 10) {
1159 /* check for previous conditions. if so add 'AND' */
1160 if(!empty($additional_where_cond)) {
1161 $additional_where_cond.= " AND ";
1164 $additional_where_cond.= "
1165 p.rating >= ". $_SESSION['rate_from'] ."
1167 p.rating <= ". $_SESSION['rate_to'] ."
1172 if(isset($_SESSION['sort_order'])) {
1173 $order_str = $this->get_sort_order();
1176 /* return a search result */
1177 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1179 SELECT DISTINCT pt1.photo_id
1181 INNER JOIN photo_tags pt2
1182 ON pt1.photo_id=pt2.photo_id
1186 ON pt1.photo_id=p.id
1189 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1191 if(!empty($additional_where_cond))
1192 $query_str.= "AND ". $additional_where_cond ." ";
1194 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1195 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1198 if(isset($order_str))
1199 $query_str.= $order_str;
1201 $result = $this->db->db_query($query_str);
1202 while($row = $this->db->db_fetch_object($result)) {
1203 array_push($matched_photos, $row['photo_id']);
1205 return $matched_photos;
1208 /* return according the selected tags */
1209 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1211 foreach($_SESSION['selected_tags'] as $tag)
1212 $selected.= $tag .",";
1213 $selected = substr($selected, 0, strlen($selected)-1);
1215 /* photo has to match at least on of the selected tags */
1216 if($_SESSION['tag_condition'] == 'or') {
1218 SELECT DISTINCT pt1.photo_id
1220 INNER JOIN photo_tags pt2
1221 ON pt1.photo_id=pt2.photo_id
1225 ON pt1.photo_id=p.id
1226 WHERE pt1.tag_id IN (". $selected .")
1228 if(!empty($additional_where_cond))
1229 $query_str.= "AND ". $additional_where_cond ." ";
1231 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1232 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1235 if(isset($order_str))
1236 $query_str.= $order_str;
1238 /* photo has to match all selected tags */
1239 elseif($_SESSION['tag_condition'] == 'and') {
1241 if(count($_SESSION['selected_tags']) >= 32) {
1242 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1243 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1247 /* Join together a table looking like
1249 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1251 so the query can quickly return all images matching the
1252 selected tags in an AND condition
1257 SELECT DISTINCT pt1.photo_id
1261 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1268 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1270 INNER JOIN photo_tags pt". ($i+2) ."
1271 ON pt1.photo_id=pt". ($i+2) .".photo_id
1276 ON pt1.photo_id=p.id
1278 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1279 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1281 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1284 if(!empty($additional_where_cond))
1285 $query_str.= "AND ". $additional_where_cond;
1287 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1288 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1291 if(isset($order_str))
1292 $query_str.= $order_str;
1296 $result = $this->db->db_query($query_str);
1297 while($row = $this->db->db_fetch_object($result)) {
1298 array_push($matched_photos, $row['photo_id']);
1300 return $matched_photos;
1303 /* return all available photos */
1305 SELECT DISTINCT p.id
1307 LEFT JOIN photo_tags pt
1313 if(!empty($additional_where_cond))
1314 $query_str.= "WHERE ". $additional_where_cond ." ";
1316 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1317 if(!empty($additional_where_cond))
1318 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1320 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1323 if(isset($order_str))
1324 $query_str.= $order_str;
1326 $result = $this->db->db_query($query_str);
1327 while($row = $this->db->db_fetch_object($result)) {
1328 array_push($matched_photos, $row['id']);
1330 return $matched_photos;
1332 } // getPhotoSelection()
1335 * control HTML ouput for photo index
1337 * this function provides all the necessary information
1338 * for the photo index template.
1341 public function showPhotoIndex()
1343 $photos = $this->getPhotoSelection();
1344 $current_tags = $this->getCurrentTags();
1346 $count = count($photos);
1348 /* if all thumbnails should be shown on one page */
1349 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1353 /* thumbnails should be splitted up in several pages */
1354 elseif($this->cfg->thumbs_per_page > 0) {
1356 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1360 $begin_with = $_SESSION['begin_with'];
1363 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1367 $images[$thumbs] = Array();
1368 $img_height[$thumbs] = Array();
1369 $img_width[$thumbs] = Array();
1370 $img_id[$thumbs] = Array();
1371 $img_name[$thumbs] = Array();
1372 $img_fullname[$thumbs] = Array();
1373 $img_title = Array();
1374 $img_rating = Array();
1376 for($i = $begin_with; $i < $end_with; $i++) {
1378 if(isset($photos[$i])) {
1380 $images[$thumbs] = $photos[$i];
1381 $img_id[$thumbs] = $i;
1382 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1383 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1384 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1385 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1387 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1389 if(file_exists($thumb_path)) {
1390 $info = getimagesize($thumb_path);
1391 $img_width[$thumbs] = $info[0];
1392 $img_height[$thumbs] = $info[1];
1398 // +1 for for smarty's selection iteration
1401 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1402 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1404 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1405 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1406 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1409 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1410 $this->tmpl->assign('tag_result', 1);
1413 /* do we have to display the page selector ? */
1414 if($this->cfg->thumbs_per_page != 0) {
1418 /* calculate the page switchers */
1419 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1420 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1422 if($begin_with != 0)
1423 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1424 if($end_with < $count)
1425 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1427 $photo_per_page = $this->cfg->thumbs_per_page;
1428 $last_page = ceil($count / $photo_per_page);
1430 /* get the current selected page */
1431 if($begin_with == 0) {
1435 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1442 for($i = 1; $i <= $last_page; $i++) {
1444 if($current_page == $i)
1445 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1446 elseif($current_page-1 == $i || $current_page+1 == $i)
1447 $style = "style=\"font-size: 105%;\"";
1448 elseif(($current_page-5 >= $i) && ($i != 1) ||
1449 ($current_page+5 <= $i) && ($i != $last_page))
1450 $style = "style=\"font-size: 75%;\"";
1454 $start_with = ($i*$photo_per_page)-$photo_per_page;
1456 if($this->is_user_friendly_url()) {
1457 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1460 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1462 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1466 $select.= ">". $i ."</a> ";
1468 // until 9 pages we show the selector from 1-9
1469 if($last_page <= 9) {
1470 $page_select.= $select;
1473 if($i == 1 /* first page */ ||
1474 $i == $last_page /* last page */ ||
1475 $i == $current_page /* current page */ ||
1476 $i == ceil($last_page * 0.25) /* first quater */ ||
1477 $i == ceil($last_page * 0.5) /* half */ ||
1478 $i == ceil($last_page * 0.75) /* third quater */ ||
1479 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1480 (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 */ ||
1481 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1482 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1484 $page_select.= $select;
1492 $page_select.= "......... ";
1497 /* only show the page selector if we have more then one page */
1499 $this->tmpl->assign('page_selector', $page_select);
1502 $extern_link = "index.php?mode=showpi";
1503 $rss_link = "index.php?mode=rss";
1504 if($current_tags != "") {
1505 $extern_link.= "&tags=". $current_tags;
1506 $rss_link.= "&tags=". $current_tags;
1508 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1509 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1510 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1513 $export_link = "index.php?mode=export";
1514 $slideshow_link = "index.php?mode=slideshow";
1516 $this->tmpl->assign('extern_link', $extern_link);
1517 $this->tmpl->assign('slideshow_link', $slideshow_link);
1518 $this->tmpl->assign('export_link', $export_link);
1519 $this->tmpl->assign('rss_link', $rss_link);
1520 $this->tmpl->assign('count', $count);
1521 $this->tmpl->assign('width', $this->cfg->thumb_width);
1522 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1523 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1524 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1525 $this->tmpl->assign('images', $images);
1526 $this->tmpl->assign('img_width', $img_width);
1527 $this->tmpl->assign('img_height', $img_height);
1528 $this->tmpl->assign('img_id', $img_id);
1529 $this->tmpl->assign('img_name', $img_name);
1530 $this->tmpl->assign('img_fullname', $img_fullname);
1531 $this->tmpl->assign('img_title', $img_title);
1532 $this->tmpl->assign('img_rating', $img_rating);
1533 $this->tmpl->assign('thumbs', $thumbs);
1534 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1536 $result = $this->tmpl->fetch("photo_index.tpl");
1538 /* if we are returning to photo index from an photo-view,
1539 scroll the window to the last shown photo-thumbnail.
1540 after this, unset the last_photo session variable.
1542 if(isset($_SESSION['last_photo'])) {
1543 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1544 unset($_SESSION['last_photo']);
1549 } // showPhotoIndex()
1552 * show credit template
1554 public function showCredits()
1556 $this->tmpl->assign('version', $this->cfg->version);
1557 $this->tmpl->assign('product', $this->cfg->product);
1558 $this->tmpl->assign('db_version', $this->dbver);
1559 $this->tmpl->show("credits.tpl");
1564 * create thumbnails for the requested width
1566 * this function creates image thumbnails of $orig_image
1567 * stored as $thumb_image. It will check if the image is
1568 * in a supported format, if necessary rotate the image
1569 * (based on EXIF orientation meta headers) and re-sizing.
1570 * @param string $orig_image
1571 * @param string $thumb_image
1572 * @param integer $width
1575 public function create_thumbnail($orig_image, $thumb_image, $width)
1577 if(!file_exists($orig_image)) {
1581 $mime = $this->get_mime_info($orig_image);
1583 /* check if original photo is a support image type */
1584 if(!$this->checkifImageSupported($mime))
1591 $meta = $this->get_meta_informations($orig_image);
1597 if(isset($meta['Orientation'])) {
1598 switch($meta['Orientation']) {
1599 case 1: /* top, left */
1600 /* nothing to do */ break;
1601 case 2: /* top, right */
1602 $rotate = 0; $flip_hori = true; break;
1603 case 3: /* bottom, left */
1604 $rotate = 180; break;
1605 case 4: /* bottom, right */
1606 $flip_vert = true; break;
1607 case 5: /* left side, top */
1608 $rotate = 90; $flip_vert = true; break;
1609 case 6: /* right side, top */
1610 $rotate = 90; break;
1611 case 7: /* left side, bottom */
1612 $rotate = 270; $flip_vert = true; break;
1613 case 8: /* right side, bottom */
1614 $rotate = 270; break;
1618 $src_img = @imagecreatefromjpeg($orig_image);
1624 $src_img = @imagecreatefrompng($orig_image);
1628 case 'image/x-portable-pixmap':
1630 $src_img = new Imagick($orig_image);
1631 $handler = "imagick";
1636 if(!isset($src_img) || empty($src_img)) {
1637 print "Can't load image from ". $orig_image ."\n";
1645 /* grabs the height and width */
1646 $cur_width = imagesx($src_img);
1647 $cur_height = imagesy($src_img);
1649 // If requested width is more then the actual image width,
1650 // do not generate a thumbnail, instead safe the original
1651 // as thumbnail but with lower quality. But if the image
1652 // is to heigh too, then we still have to resize it.
1653 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1654 $result = imagejpeg($src_img, $thumb_image, 75);
1655 imagedestroy($src_img);
1662 $cur_width = $src_img->getImageWidth();
1663 $cur_height = $src_img->getImageHeight();
1665 // If requested width is more then the actual image width,
1666 // do not generate a thumbnail, instead safe the original
1667 // as thumbnail but with lower quality. But if the image
1668 // is to heigh too, then we still have to resize it.
1669 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1670 $src_img->setCompressionQuality(75);
1671 $src_img->setImageFormat('jpeg');
1672 $src_img->writeImage($thumb_image);
1674 $src_img->destroy();
1681 // If the image will be rotate because EXIF orientation said so
1682 // 'virtually rotate' the image for further calculations
1683 if($rotate == 90 || $rotate == 270) {
1685 $cur_width = $cur_height;
1689 /* calculates aspect ratio */
1690 $aspect_ratio = $cur_height / $cur_width;
1693 if($aspect_ratio < 1) {
1695 $new_h = abs($new_w * $aspect_ratio);
1697 /* 'virtually' rotate the image and calculate it's ratio */
1698 $tmp_w = $cur_height;
1699 $tmp_h = $cur_width;
1700 /* now get the ratio from the 'rotated' image */
1701 $tmp_ratio = $tmp_h/$tmp_w;
1702 /* now calculate the new dimensions */
1704 $tmp_h = abs($tmp_w * $tmp_ratio);
1706 // now that we know, how high they photo should be, if it
1707 // gets rotated, use this high to scale the image
1709 $new_w = abs($new_h / $aspect_ratio);
1711 // If the image will be rotate because EXIF orientation said so
1712 // now 'virtually rotate' back the image for the image manipulation
1713 if($rotate == 90 || $rotate == 270) {
1724 /* creates new image of that size */
1725 $dst_img = imagecreatetruecolor($new_w, $new_h);
1727 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1729 /* copies resized portion of original image into new image */
1730 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1732 /* needs the image to be flipped horizontal? */
1734 $this->_debug("(FLIP)");
1735 $dst_img = $this->flipImage($dst_img, 'hori');
1737 /* needs the image to be flipped vertical? */
1739 $this->_debug("(FLIP)");
1740 $dst_img = $this->flipImage($dst_img, 'vert');
1744 $this->_debug("(ROTATE)");
1745 $dst_img = $this->rotateImage($dst_img, $rotate);
1748 /* write down new generated file */
1749 $result = imagejpeg($dst_img, $thumb_image, 75);
1751 /* free your mind */
1752 imagedestroy($dst_img);
1753 imagedestroy($src_img);
1755 if($result === false) {
1756 print "Can't write thumbnail ". $thumb_image ."\n";
1766 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1768 /* needs the image to be flipped horizontal? */
1770 $this->_debug("(FLIP)");
1771 $src_img->rotateImage(new ImagickPixel(), 90);
1772 $src_img->flipImage();
1773 $src_img->rotateImage(new ImagickPixel(), -90);
1775 /* needs the image to be flipped vertical? */
1777 $this->_debug("(FLIP)");
1778 $src_img->flipImage();
1782 $this->_debug("(ROTATE)");
1783 $src_img->rotateImage(new ImagickPixel(), $rotate);
1786 $src_img->setCompressionQuality(75);
1787 $src_img->setImageFormat('jpeg');
1789 if(!$src_img->writeImage($thumb_image)) {
1790 print "Can't write thumbnail ". $thumb_image ."\n";
1795 $src_img->destroy();
1802 } // create_thumbnail()
1805 * return all exif meta data from the file
1806 * @param string $file
1809 public function get_meta_informations($file)
1811 return exif_read_data($file);
1813 } // get_meta_informations()
1816 * create phpfspot own sqlite database
1818 * this function creates phpfspots own sqlite database
1819 * if it does not exist yet. this own is used to store
1820 * some necessary informations (md5 sum's, ...).
1822 public function check_config_table()
1824 // if the config table doesn't exist yet, create it
1825 if(!$this->cfg_db->db_check_table_exists("images")) {
1826 $this->cfg_db->db_exec("
1827 CREATE TABLE images (
1828 img_idx int primary key,
1834 } // check_config_table
1837 * Generates a thumbnail from photo idx
1839 * This function will generate JPEG thumbnails from provided F-Spot photo
1842 * 1. Check if all thumbnail generations (width) are already in place and
1844 * 2. Check if the md5sum of the original file has changed
1845 * 3. Generate the thumbnails if needed
1846 * @param integer $idx
1847 * @param integer $force
1848 * @param boolean $overwrite
1850 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1854 $resolutions = Array(
1855 $this->cfg->thumb_width,
1856 $this->cfg->photo_width,
1857 $this->cfg->mini_width,
1861 /* get details from F-Spot's database */
1862 $details = $this->get_photo_details($idx);
1864 /* calculate file MD5 sum */
1865 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1867 if(!file_exists($full_path)) {
1868 $this->_error("File ". $full_path ." does not exist\n");
1872 if(!is_readable($full_path)) {
1873 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1877 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1879 /* If Nikon NEF format, we need to treat it another way */
1880 if(isset($this->cfg->dcraw_bin) &&
1881 file_exists($this->cfg->dcraw_bin) &&
1882 is_executable($this->cfg->dcraw_bin) &&
1883 preg_match('/\.nef$/i', $details['uri'])) {
1885 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1887 /* if PPM file does not exist, let dcraw convert it from NEF */
1888 if(!file_exists($ppm_path)) {
1889 system($this->cfg->dcraw_bin ." -a ". $full_path);
1892 /* for now we handle the PPM instead of the NEF */
1893 $full_path = $ppm_path;
1897 $file_md5 = md5_file($full_path);
1900 foreach($resolutions as $resolution) {
1902 $generate_it = false;
1904 $thumb_sub_path = substr($file_md5, 0, 2);
1905 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1907 /* if thumbnail-subdirectory does not exist yet, create it */
1908 if(!file_exists(dirname($thumb_path))) {
1909 mkdir(dirname($thumb_path), 0755);
1912 /* if the thumbnail file doesn't exist, create it */
1913 if(!file_exists($thumb_path)) {
1914 $generate_it = true;
1916 /* if the file hasn't changed there is no need to regen the thumb */
1917 elseif($file_md5 != $this->getMD5($idx) || $force) {
1918 $generate_it = true;
1921 if($generate_it || $overwrite) {
1923 $this->_debug(" ". $resolution ."px");
1924 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1932 $this->_debug(" already exist");
1935 /* set the new/changed MD5 sum for the current photo */
1937 $this->setMD5($idx, $file_md5);
1940 $this->_debug("\n");
1945 * returns stored md5 sum for a specific photo
1947 * this function queries the phpfspot database for a
1948 * stored MD5 checksum of the specified photo
1949 * @param integer $idx
1950 * @return string|null
1952 public function getMD5($idx)
1954 $result = $this->cfg_db->db_query("
1957 WHERE img_idx='". $idx ."'
1963 $img = $this->cfg_db->db_fetch_object($result);
1964 return $img['img_md5'];
1969 * set MD5 sum for the specific photo
1970 * @param integer $idx
1971 * @param string $md5
1973 private function setMD5($idx, $md5)
1975 $result = $this->cfg_db->db_exec("
1976 REPLACE INTO images (img_idx, img_md5)
1977 VALUES ('". $idx ."', '". $md5 ."')
1983 * store current tag condition
1985 * this function stores the current tag condition
1986 * (AND or OR) in the users session variables
1987 * @param string $mode
1990 public function setTagCondition($mode)
1992 $_SESSION['tag_condition'] = $mode;
1996 } // setTagCondition()
1999 * invoke tag & date search
2001 * this function will return all matching tags and store
2002 * them in the session variable selected_tags. furthermore
2003 * it also handles the date search.
2004 * getPhotoSelection() will then only return the matching
2008 public function startSearch()
2011 if(isset($_POST['date_from']) && $this->isValidDate($_POST['date_from'])) {
2012 $date_from = $_POST['date_from'];
2014 if(isset($_POST['date_to']) && $this->isValidDate($_POST['date_to'])) {
2015 $date_to = $_POST['date_to'];
2018 /* tag-name search */
2019 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2020 $searchfor_tag = $_POST['for_tag'];
2021 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2024 unset($_SESSION['searchfor_tag']);
2027 /* file-name search */
2028 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2029 $_SESSION['searchfor_name'] = $_POST['for_name'];
2032 unset($_SESSION['searchfor_name']);
2036 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2038 $_SESSION['rate_from'] = $_POST['rate_from'];
2040 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2041 $_SESSION['rate_to'] = $_POST['rate_to'];
2045 /* delete any previously set value */
2046 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2051 if(isset($date_from) && !empty($date_from))
2052 $_SESSION['from_date'] = strtotime($date_from ." 00:00:00");
2054 unset($_SESSION['from_date']);
2056 if(isset($date_to) && !empty($date_to))
2057 $_SESSION['to_date'] = strtotime($date_to ." 23:59:59");
2059 unset($_SESSION['to_date']);
2061 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2062 /* new search, reset the current selected tags */
2063 $_SESSION['selected_tags'] = Array();
2064 foreach($this->avail_tags as $tag) {
2065 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2066 array_push($_SESSION['selected_tags'], $tag);
2075 * updates sort order in session variable
2077 * this function is invoked by RPC and will sort the requested
2078 * sort order in the session variable.
2079 * @param string $sort_order
2082 public function updateSortOrder($order)
2084 if(isset($this->sort_orders[$order])) {
2085 $_SESSION['sort_order'] = $order;
2089 return "unkown error";
2091 } // updateSortOrder()
2096 * this function rotates the image according the
2098 * @param string $img
2099 * @param integer $degress
2102 private function rotateImage($img, $degrees)
2104 if(function_exists("imagerotate")) {
2105 $img = imagerotate($img, $degrees, 0);
2107 function imagerotate($src_img, $angle)
2109 $src_x = imagesx($src_img);
2110 $src_y = imagesy($src_img);
2111 if ($angle == 180) {
2115 elseif ($src_x <= $src_y) {
2119 elseif ($src_x >= $src_y) {
2124 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2125 imagealphablending($rotate, false);
2130 for ($y = 0; $y < ($src_y); $y++) {
2131 for ($x = 0; $x < ($src_x); $x++) {
2132 $color = imagecolorat($src_img, $x, $y);
2133 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2139 for ($y = 0; $y < ($src_y); $y++) {
2140 for ($x = 0; $x < ($src_x); $x++) {
2141 $color = imagecolorat($src_img, $x, $y);
2142 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2148 for ($y = 0; $y < ($src_y); $y++) {
2149 for ($x = 0; $x < ($src_x); $x++) {
2150 $color = imagecolorat($src_img, $x, $y);
2151 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2165 $img = imagerotate($img, $degrees);
2174 * returns flipped image
2176 * this function will return an either horizontal or
2177 * vertical flipped truecolor image.
2178 * @param string $image
2179 * @param string $mode
2182 private function flipImage($image, $mode)
2184 $w = imagesx($image);
2185 $h = imagesy($image);
2186 $flipped = imagecreatetruecolor($w, $h);
2190 for ($y = 0; $y < $h; $y++) {
2191 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2195 for ($x = 0; $x < $w; $x++) {
2196 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2206 * return all assigned tags for the specified photo
2207 * @param integer $idx
2210 private function get_photo_tags($idx)
2212 $result = $this->db->db_query("
2215 INNER JOIN photo_tags pt
2217 WHERE pt.photo_id='". $idx ."'
2222 while($row = $this->db->db_fetch_object($result)) {
2223 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2225 $tags[$row['id']] = $row['name'];
2230 } // get_photo_tags()
2233 * create on-the-fly images with text within
2234 * @param string $txt
2235 * @param string $color
2236 * @param integer $space
2237 * @param integer $font
2240 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2242 if (strlen($color) != 6)
2245 $int = hexdec($color);
2246 $h = imagefontheight($font);
2247 $fw = imagefontwidth($font);
2248 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2249 $lines = count($txt);
2250 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2251 $bg = imagecolorallocate($im, 255, 255, 255);
2252 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2255 foreach ($txt as $text) {
2256 $x = (($w - ($fw * strlen($text))) / 2);
2257 imagestring($im, $font, $x, $y, $text, $color);
2258 $y += ($h + $space);
2261 Header("Content-type: image/png");
2264 } // showTextImage()
2267 * check if all requirements are met
2270 private function check_requirements()
2272 if(!function_exists("imagecreatefromjpeg")) {
2273 print "PHP GD library extension is missing<br />\n";
2277 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2278 print "PHP SQLite3 library extension is missing<br />\n";
2282 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2283 ini_set('track_errors', 1);
2284 @include_once 'HTML/AJAX/Server.php';
2285 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2286 print "PEAR HTML_AJAX package is missing<br />\n";
2289 @include_once 'Calendar/Calendar.php';
2290 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2291 print "PEAR Calendar package is missing<br />\n";
2294 @include_once 'Console/Getopt.php';
2295 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2296 print "PEAR Console_Getopt package is missing<br />\n";
2299 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2300 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2301 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2304 ini_restore('track_errors');
2311 } // check_requirements()
2313 private function _debug($text)
2315 if(isset($this->fromcmd)) {
2322 * check if specified MIME type is supported
2323 * @param string $mime
2326 public function checkifImageSupported($mime)
2328 $supported_types = Array(
2331 "image/x-portable-pixmap",
2335 if(in_array($mime, $supported_types))
2340 } // checkifImageSupported()
2344 * @param string $text
2346 public function _error($text)
2348 switch($this->cfg->logging) {
2351 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2352 print $text ."<br />\n";
2358 error_log($text, 3, $his->cfg->log_file);
2362 $this->runtime_error = true;
2367 * get calendar input-text fields
2369 * this function returns a text-field used for the data selection.
2370 * Either it will be filled with the current date or, if available,
2371 * filled with the date user entered previously.
2373 * @param string $mode
2376 private function get_date_text_field($mode)
2378 $date = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2380 $date.= isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2382 $date.= isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2384 $output = "<input type=\"text\" size=\"15\" id=\"date_". $mode ."\" value=\"". $date ."\"";
2385 if(!isset($_SESSION[$mode .'_date']))
2386 $output.= " disabled=\"disabled\"";
2391 } // get_date_text_field()
2394 * output calendar matrix
2395 * @param integer $year
2396 * @param integer $month
2397 * @param integer $day
2399 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2401 if (!isset($year)) $year = date('Y');
2402 if (!isset($month)) $month = date('m');
2403 if (!isset($day)) $day = date('d');
2408 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2409 require_once CALENDAR_ROOT.'Day.php';
2412 $month = new Calendar_Month_Weekdays($year,$month);
2415 $prevStamp = $month->prevMonth(true);
2416 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2417 $nextStamp = $month->nextMonth(true);
2418 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2420 $selectedDays = array (
2421 new Calendar_Day($year,$month,$day),
2422 new Calendar_Day($year,12,25),
2425 // Build the days in the month
2426 $month->build($selectedDays);
2428 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2429 $this->tmpl->assign('prev_month', $prev);
2430 $this->tmpl->assign('next_month', $next);
2432 while ( $day = $month->fetch() ) {
2434 if(!isset($matrix[$rows]))
2435 $matrix[$rows] = Array();
2439 $dayStamp = $day->thisDay(true);
2440 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2442 // isFirst() to find start of week
2443 if ( $day->isFirst() )
2446 if ( $day->isSelected() ) {
2447 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2448 } else if ( $day->isEmpty() ) {
2449 $string.= "<td> </td>\n";
2451 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2454 // isLast() to find end of week
2455 if ( $day->isLast() )
2456 $string.= "</tr>\n";
2458 $matrix[$rows][$cols] = $string;
2468 $this->tmpl->assign('matrix', $matrix);
2469 $this->tmpl->assign('rows', $rows);
2470 $this->tmpl->show("calendar.tpl");
2472 } // get_calendar_matrix()
2475 * output export page
2476 * @param string $mode
2478 public function getExport($mode)
2480 $pictures = $this->getPhotoSelection();
2481 $current_tags = $this->getCurrentTags();
2483 foreach($pictures as $picture) {
2485 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2486 if($current_tags != "") {
2487 $orig_url.= "&tags=". $current_tags;
2489 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2490 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2493 if($this->is_user_friendly_url()) {
2494 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2497 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2503 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2504 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2508 // "[%pictureurl% %thumbnailurl%]"
2509 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2512 case 'MoinMoinList':
2513 // " * [%pictureurl% %thumbnailurl%]"
2514 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2525 public function getRSSFeed()
2527 Header("Content-type: text/xml; charset=utf-8");
2528 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2531 xmlns:media="http://search.yahoo.com/mrss/"
2532 xmlns:dc="http://purl.org/dc/elements/1.1/"
2535 <title>phpfspot</title>
2536 <description>phpfspot RSS feed</description>
2537 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2538 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2539 <generator>phpfspot</generator>
2542 $pictures = $this->getPhotoSelection();
2543 $current_tags = $this->getCurrentTags();
2545 foreach($pictures as $picture) {
2547 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2548 if($current_tags != "") {
2549 $orig_url.= "&tags=". $current_tags;
2551 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2552 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2555 $details = $this->get_photo_details($picture);
2557 if($this->is_user_friendly_url()) {
2558 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2561 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2564 $thumb_html = htmlspecialchars("
2565 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2567 ". $details['description']);
2569 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2571 /* get EXIF information if JPEG */
2572 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2573 $meta = $this->get_meta_informations($orig_path);
2578 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2579 <link><?php print htmlspecialchars($orig_url); ?></link>
2580 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2581 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $details['time']); ?></dc:date.Taken>
2583 <?php print $thumb_html; ?>
2585 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $details['time']); ?></pubDate>
2600 * get all selected tags
2602 * This function will return all selected tags as one string, seperated
2606 private function getCurrentTags()
2609 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2610 foreach($_SESSION['selected_tags'] as $tag)
2611 $current_tags.= $tag .",";
2612 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2614 return $current_tags;
2616 } // getCurrentTags()
2619 * return the current photo
2621 public function getCurrentPhoto()
2623 if(isset($_SESSION['current_photo'])) {
2624 print $_SESSION['current_photo'];
2626 } // getCurrentPhoto()
2629 * tells the client browser what to do
2631 * this function is getting called via AJAX by the
2632 * client browsers. it will tell them what they have
2633 * to do next. This is necessary for directly jumping
2634 * into photo index or single photo view when the are
2635 * requested with specific URLs
2638 public function whatToDo()
2640 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2642 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2643 return "showpi_tags";
2645 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2652 * return the current process-user
2655 private function getuid()
2657 if($uid = posix_getuid()) {
2658 if($user = posix_getpwuid($uid)) {
2659 return $user['name'];
2668 * returns a select-dropdown box to select photo index sort parameters
2669 * @param array $params
2670 * @param smarty $smarty
2673 public function smarty_sort_select_list($params, &$smarty)
2677 foreach($this->sort_orders as $key => $value) {
2678 $output.= "<option value=\"". $key ."\"";
2679 if($key == $_SESSION['sort_order']) {
2680 $output.= " selected=\"selected\"";
2682 $output.= ">". $value ."</option>";
2687 } // smarty_sort_select_list()
2690 * returns the currently selected sort order
2693 private function get_sort_order()
2695 switch($_SESSION['sort_order']) {
2697 return " ORDER BY p.time ASC";
2700 return " ORDER BY p.time DESC";
2703 if($this->dbver < 9) {
2704 return " ORDER BY p.name ASC";
2707 return " ORDER BY basename(p.uri) ASC";
2711 if($this->dbver < 9) {
2712 return " ORDER BY p.name DESC";
2715 return " ORDER BY basename(p.uri) DESC";
2719 return " ORDER BY t.name ASC ,p.time ASC";
2722 return " ORDER BY t.name DESC ,p.time ASC";
2725 return " ORDER BY t.name ASC, p.rating ASC";
2728 return " ORDER BY t.name DESC, p.rating DESC";
2732 } // get_sort_order()
2735 * return the next to be shown slide show image
2737 * this function returns the URL of the next image
2738 * in the slideshow sequence.
2741 public function getNextSlideShowImage()
2743 $all_photos = $this->getPhotoSelection();
2745 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2746 $_SESSION['slideshow_img'] = 0;
2748 $_SESSION['slideshow_img']++;
2750 if($this->is_user_friendly_url()) {
2751 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2754 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2756 } // getNextSlideShowImage()
2759 * return the previous to be shown slide show image
2761 * this function returns the URL of the previous image
2762 * in the slideshow sequence.
2765 public function getPrevSlideShowImage()
2767 $all_photos = $this->getPhotoSelection();
2769 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2770 $_SESSION['slideshow_img'] = 0;
2772 $_SESSION['slideshow_img']--;
2774 if($this->is_user_friendly_url()) {
2775 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2778 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2780 } // getPrevSlideShowImage()
2782 public function resetSlideShow()
2784 if(isset($_SESSION['slideshow_img']))
2785 unset($_SESSION['slideshow_img']);
2787 } // resetSlideShow()
2792 * this function will get all photos from the fspot
2793 * database and randomly return ONE entry
2795 * saddly there is yet no sqlite3 function which returns
2796 * the bulk result in array, so we have to fill up our
2800 public function get_random_photo()
2809 /* if show_tags is set, only return details for photos which
2810 are specified to be shown
2812 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2814 INNER JOIN photo_tags pt
2819 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2822 $result = $this->db->db_query($query_str);
2824 while($row = $this->db->db_fetch_object($result)) {
2825 array_push($all, $row['id']);
2828 return $all[array_rand($all)];
2830 } // get_random_photo()
2833 * get random photo tag photo
2835 * this function will get all photos tagged with the requested
2836 * tag from the fspot database and randomly return ONE entry
2838 * saddly there is yet no sqlite3 function which returns
2839 * the bulk result in array, so we have to fill up our
2843 public function get_random_tag_photo($tagidx)
2850 INNER JOIN photo_tags pt
2854 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2862 pt.tag_id LIKE '". $tagidx ."'
2865 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2868 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2872 $result = $this->db->db_query($query_str);
2874 while($row = $this->db->db_fetch_object($result)) {
2875 array_push($all, $row['id']);
2878 return $all[array_rand($all)];
2880 } // get_random_tag_photo()
2883 * validates provided date
2885 * this function validates if the provided date
2886 * contains a valid date and will return true
2888 * @param string $date_str
2891 public function isValidDate($date_str)
2893 $timestamp = strtotime($date_str);
2895 if(is_numeric($timestamp))
2903 * timestamp to string conversion
2904 * @param integer $timestamp
2907 private function ts2str($timestamp)
2909 if(!empty($timestamp) && is_numeric($timestamp))
2910 return strftime("%Y-%m-%d", $timestamp);
2915 * extract tag-names from $_GET['tags']
2916 * @param string $tags_str
2919 private function extractTags($tags_str)
2921 $not_validated = split(',', $tags_str);
2922 $validated = array();
2924 foreach($not_validated as $tag) {
2925 if(is_numeric($tag))
2926 array_push($validated, $tag);
2934 * returns the full path to a thumbnail
2935 * @param integer $width
2936 * @param integer $photo
2939 public function get_thumb_path($width, $photo)
2941 $md5 = $this->getMD5($photo);
2942 $sub_path = substr($md5, 0, 2);
2943 return $this->cfg->thumb_path
2951 } // get_thumb_path()
2954 * returns server's virtual host name
2957 private function get_server_name()
2959 return $_SERVER['SERVER_NAME'];
2960 } // get_server_name()
2963 * returns type of webprotocol which is currently used
2966 private function get_web_protocol()
2968 if(!isset($_SERVER['HTTPS']))
2972 } // get_web_protocol()
2975 * return url to this phpfspot installation
2978 private function get_phpfspot_url()
2980 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2982 } // get_phpfspot_url()
2985 * returns the number of photos which are tagged with $tag_id
2986 * @param integer $tag_id
2989 public function get_num_photos($tag_id)
2991 if($result = $this->db->db_fetchSingleRow("
2992 SELECT count(*) as number
2995 tag_id LIKE '". $tag_id ."'")) {
2997 return $result['number'];
3003 } // get_num_photos()
3006 * check file exists and is readable
3008 * returns true, if everything is ok, otherwise false
3009 * if $silent is not set, this function will output and
3011 * @param string $file
3012 * @param boolean $silent
3015 private function check_readable($file, $silent = null)
3017 if(!file_exists($file)) {
3019 print "File \"". $file ."\" does not exist.\n";
3023 if(!is_readable($file)) {
3025 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3031 } // check_readable()
3034 * check if all needed indices are present
3036 * this function checks, if some needed indices are already
3037 * present, or if not, create them on the fly. they are
3038 * necessary to speed up some queries like that one look for
3039 * all tags, when show_tags is specified in the configuration.
3041 private function checkDbIndices()
3043 $result = $this->db->db_exec("
3044 CREATE INDEX IF NOT EXISTS
3051 } // checkDbIndices()
3054 * retrive F-Spot database version
3056 * this function will return the F-Spot database version number
3057 * It is stored within the sqlite3 database in the table meta
3058 * @return string|null
3060 public function getFspotDBVersion()
3062 if($result = $this->db->db_fetchSingleRow("
3063 SELECT data as version
3066 name LIKE 'F-Spot Database Version'
3068 return $result['version'];
3072 } // getFspotDBVersion()
3075 * parse the provided URI and will returned the requested chunk
3076 * @param string $uri
3077 * @param string $mode
3080 public function parse_uri($uri, $mode)
3082 if(($components = parse_url($uri)) !== false) {
3086 return basename($components['path']);
3089 return dirname($components['path']);
3092 return $components['path'];
3102 * validate config options
3104 * this function checks if all necessary configuration options are
3105 * specified and set.
3108 private function check_config_options()
3110 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3111 $this->_error("Please set \$page_title in phpfspot_cfg");
3113 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3114 $this->_error("Please set \$base_path in phpfspot_cfg");
3116 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3117 $this->_error("Please set \$web_path in phpfspot_cfg");
3119 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3120 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3122 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3123 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3125 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3126 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3128 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3129 $this->_error("Please set \$db_access in phpfspot_cfg");
3131 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3132 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3134 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3135 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3137 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3138 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3140 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3141 $this->_error("Please set \$photo_width in phpfspot_cfg");
3143 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3144 $this->_error("Please set \$mini_width in phpfspot_cfg");
3146 if(!isset($this->cfg->thumbs_per_page))
3147 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3149 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3150 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3152 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3153 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3155 if(!isset($this->cfg->hide_tags))
3156 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3158 if(!isset($this->cfg->theme_name))
3159 $this->_error("Please set \$theme_name in phpfspot_cfg");
3161 if(!isset($this->cfg->logging))
3162 $this->_error("Please set \$logging in phpfspot_cfg");
3164 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3166 if(!isset($this->cfg->log_file))
3167 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3169 if(!is_writeable($this->cfg->log_file))
3170 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3174 /* remove trailing slash, if set */
3175 if($this->cfg->web_path == "/")
3176 $this->cfg->web_path = "";
3177 elseif(preg_match('/\/$/', $this->cfg->web_path))
3178 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3180 return $this->runtime_error;
3182 } // check_config_options()
3185 * cleanup phpfspot own database
3187 * When photos are getting delete from F-Spot, there will remain
3188 * remain some residues in phpfspot own database. This function
3189 * will try to wipe them out.
3191 public function cleanup_phpfspot_db()
3193 $to_delete = Array();
3195 $result = $this->cfg_db->db_query("
3198 ORDER BY img_idx ASC
3201 while($row = $this->cfg_db->db_fetch_object($result)) {
3202 if(!$this->db->db_fetchSingleRow("
3205 WHERE id='". $row['img_idx'] ."'")) {
3207 array_push($to_delete, $row['img_idx'], ',');
3211 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3213 $this->cfg_db->db_exec("
3215 WHERE img_idx IN (". implode($to_delete) .")
3218 } // cleanup_phpfspot_db()
3221 * return first image of the page, the $current photo
3224 * this function is used to find out the first photo of the
3225 * current page, in which the $current photo lies. this is
3226 * used to display the correct photo, when calling showPhotoIndex()
3228 * @param integer $current
3229 * @param integer $max
3232 private function getCurrentPage($current, $max)
3234 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3235 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3236 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3242 } // getCurrentPage()
3247 * this function tries to find out the correct mime-type
3248 * for the provided file.
3249 * @param string $file
3252 public function get_mime_info($file)
3254 $details = getimagesize($file);
3256 /* if getimagesize() returns empty, try at least to find out the
3259 if(empty($details) && function_exists('mime_content_type')) {
3261 // mime_content_type is marked as deprecated in the documentation,
3262 // but is it really necessary to force users to install a PECL
3264 $details['mime'] = mime_content_type($file);
3267 return $details['mime'];
3269 } // get_mime_info()
3272 * return tag-name by tag-idx
3274 * this function returns the tag-name for the requested
3275 * tag specified by tag-idx.
3276 * @param integer $idx
3279 public function get_tag_name($idx)
3281 if($result = $this->db->db_fetchSingleRow("
3285 id LIKE '". $idx ."'")) {
3287 return $result['name'];
3296 * parse user friendly url which got rewritten by the websever
3297 * @param string $request_uri
3300 private function parse_user_friendly_url($request_uri)
3302 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3304 $options = explode('/', $request_uri);
3306 switch($options[1]) {
3308 if(is_numeric($options[2])) {
3309 $this->session_cleanup();
3310 //unset($_SESSION['start_action']);
3311 //unset($_SESSION['selected_tags']);
3312 $_GET['mode'] = 'showp';
3313 return $this->showPhoto($options[2]);
3317 if(is_numeric($options[2])) {
3318 require_once "phpfspot_img.php";
3319 $img = new PHPFSPOT_IMG;
3320 if(isset($options[3]) && is_numeric($options[3]))
3321 $img->showImg($options[2], $options[3]);
3323 $img->showImg($options[2]);
3328 if(is_numeric($options[2])) {
3329 $this->session_cleanup();
3330 $_GET['tags'] = $options[2];
3331 $_SESSION['selected_tags'] = Array($options[2]);
3332 if(isset($options[3]) && is_numeric($options[3]))
3333 $_SESSION['begin_with'] = $options[3];
3334 return $this->showPhotoIndex();
3340 } // parse_user_friendly_url()
3343 * check if user-friendly-urls are enabled
3345 * this function will return true, if the config option
3346 * $user_friendly_url has been set. Otherwise false.
3349 private function is_user_friendly_url()
3351 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3356 } // is_user_friendly_url()
3361 * this function will cleanup user's session information
3363 private function session_cleanup()
3365 unset($_SESSION['begin_with']);
3366 $this->resetDateSearch();
3367 $this->resetPhotoView();
3368 $this->resetTagSearch();
3369 $this->resetNameSearch();
3370 $this->resetDateSearch();
3373 } // session_cleanup()