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_calendar('from'));
348 $this->tmpl->assign('search_to_date', $this->get_calendar('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_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
723 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
724 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
726 $extern_link = "index.php?mode=showp&id=". $photo;
727 $current_tags = $this->getCurrentTags();
728 if($current_tags != "") {
729 $extern_link.= "&tags=". $current_tags;
731 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
732 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
735 $this->tmpl->assign('extern_link', $extern_link);
737 if(!file_exists($thumb_path)) {
738 $this->_error("Can't open file ". $thumb_path ."\n");
742 $info_thumb = getimagesize($thumb_path);
744 $this->tmpl->assign('description', $details['description']);
745 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
746 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
748 $this->tmpl->assign('width', $info_thumb[0]);
749 $this->tmpl->assign('height', $info_thumb[1]);
750 $this->tmpl->assign('ExifMadeOn', $meta_date);
751 $this->tmpl->assign('ExifMadeWith', $meta_make);
752 $this->tmpl->assign('ExifOrigResolution', $meta_res);
753 $this->tmpl->assign('ExifFileSize', $meta_size);
755 if($this->is_user_friendly_url()) {
756 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
757 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
760 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
761 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
764 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
766 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
767 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
768 $this->tmpl->assign('current_img', $photo);
770 if(isset($previous_img)) {
771 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
772 $this->tmpl->assign('prev_img', $previous_img);
775 if(isset($next_img)) {
776 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
777 $this->tmpl->assign('next_img', $next_img);
780 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
781 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
782 $this->tmpl->assign('photo_number', $i);
783 $this->tmpl->assign('photo_count', count($all_photos));
785 return $this->tmpl->fetch("single_photo.tpl");
790 * all available tags and tag cloud
792 * this function outputs all available tags (time ordered)
793 * and in addition output them as tag cloud (tags which have
794 * many photos will appears more then others)
796 public function getAvailableTags()
798 /* retrive tags from database */
803 $result = $this->db->db_query("
804 SELECT tag_id as id, count(tag_id) as quantity
814 while($row = $this->db->db_fetch_object($result)) {
815 $tags[$row['id']] = $row['quantity'];
818 // change these font sizes if you will
819 $max_size = 125; // max font size in %
820 $min_size = 75; // min font size in %
823 $max_sat = hexdec('cc');
824 $min_sat = hexdec('44');
826 // get the largest and smallest array values
827 $max_qty = max(array_values($tags));
828 $min_qty = min(array_values($tags));
830 // find the range of values
831 $spread = $max_qty - $min_qty;
832 if (0 == $spread) { // we don't want to divide by zero
836 // determine the font-size increment
837 // this is the increase per tag quantity (times used)
838 $step = ($max_size - $min_size)/($spread);
839 $step_sat = ($max_sat - $min_sat)/($spread);
841 // loop through our tag array
842 foreach ($tags as $key => $value) {
844 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
847 // calculate CSS font-size
848 // find the $value in excess of $min_qty
849 // multiply by the font-size increment ($size)
850 // and add the $min_size set above
851 $size = $min_size + (($value - $min_qty) * $step);
852 // uncomment if you want sizes in whole %:
855 $color = $min_sat + ($value - $min_qty) * $step_sat;
861 if(isset($this->tags[$key])) {
862 if($this->is_user_friendly_url())
863 $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>, ";
865 $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>, ";
869 $output = substr($output, 0, strlen($output)-2);
872 } // getAvailableTags()
875 * output all selected tags
877 * this function output all tags which have been selected
878 * by the user. the selected tags are stored in the
879 * session-variable $_SESSION['selected_tags']
882 public function getSelectedTags($type = 'link')
884 /* retrive tags from database */
889 foreach($this->avail_tags as $tag)
891 // return all selected tags
892 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
897 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
901 <div class=\"tagresulttag\">
902 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
903 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
913 $output = substr($output, 0, strlen($output)-2);
917 return "no tags selected";
920 } // getSelectedTags()
923 * add tag to users session variable
925 * this function will add the specified to users current
926 * tag selection. if a date search has been made before
927 * it will be now cleared
930 public function addTag($tag)
932 if(!isset($_SESSION['selected_tags']))
933 $_SESSION['selected_tags'] = Array();
935 if(isset($_SESSION['searchfor_tag']))
936 unset($_SESSION['searchfor_tag']);
938 // has the user requested to hide this tag, and still someone,
939 // somehow tries to add it, don't allow this.
940 if(!isset($this->cfg->hide_tags) &&
941 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
944 if(!in_array($tag, $_SESSION['selected_tags']))
945 array_push($_SESSION['selected_tags'], $tag);
952 * remove tag to users session variable
954 * this function removes the specified tag from
955 * users current tag selection
959 public function delTag($tag)
961 if(isset($_SESSION['searchfor_tag']))
962 unset($_SESSION['searchfor_tag']);
964 if(isset($_SESSION['selected_tags'])) {
965 $key = array_search($tag, $_SESSION['selected_tags']);
966 unset($_SESSION['selected_tags'][$key]);
967 sort($_SESSION['selected_tags']);
975 * reset tag selection
977 * if there is any tag selection, it will be
980 public function resetTags()
982 if(isset($_SESSION['selected_tags']))
983 unset($_SESSION['selected_tags']);
988 * returns the value for the autocomplete tag-search
991 public function get_xml_tag_list()
993 if(!isset($_GET['search']) || !is_string($_GET['search']))
994 $_GET['search'] = '';
999 /* retrive tags from database */
1002 $matched_tags = Array();
1004 header("Content-Type: text/xml");
1006 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1007 $string.= "<results>\n";
1009 foreach($this->avail_tags as $tag)
1011 if(!empty($_GET['search']) &&
1012 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1013 count($matched_tags) < $length) {
1015 $count = $this->get_num_photos($tag);
1018 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1021 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1027 /* if we have collected enough items, break out */
1028 if(count($matched_tags) >= $length)
1032 $string.= "</results>\n";
1036 } // get_xml_tag_list()
1040 * reset single photo
1042 * if a specific photo was requested (external link)
1043 * unset the session variable now
1045 public function resetPhotoView()
1047 if(isset($_SESSION['current_photo']))
1048 unset($_SESSION['current_photo']);
1050 } // resetPhotoView();
1055 * if any tag search has taken place, reset it now
1057 public function resetTagSearch()
1059 if(isset($_SESSION['searchfor_tag']))
1060 unset($_SESSION['searchfor_tag']);
1062 } // resetTagSearch()
1067 * if any name search has taken place, reset it now
1069 public function resetNameSearch()
1071 if(isset($_SESSION['searchfor_name']))
1072 unset($_SESSION['searchfor_name']);
1074 } // resetNameSearch()
1079 * if any date search has taken place, reset it now.
1081 public function resetDateSearch()
1083 if(isset($_SESSION['from_date']))
1084 unset($_SESSION['from_date']);
1085 if(isset($_SESSION['to_date']))
1086 unset($_SESSION['to_date']);
1088 } // resetDateSearch();
1093 * if any rate search has taken place, reset it now.
1095 public function resetRateSearch()
1097 if(isset($_SESSION['rate_from']))
1098 unset($_SESSION['rate_from']);
1099 if(isset($_SESSION['rate_to']))
1100 unset($_SESSION['rate_to']);
1102 } // resetRateSearch();
1105 * return all photo according selection
1107 * this function returns all photos based on
1108 * the tag-selection, tag- or date-search.
1109 * the tag-search also has to take care of AND
1110 * and OR conjunctions
1113 public function getPhotoSelection()
1115 $matched_photos = Array();
1116 $additional_where_cond = "";
1118 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1119 $from_date = $_SESSION['from_date'];
1120 $to_date = $_SESSION['to_date'];
1121 $additional_where_cond.= "
1122 p.time>='". $from_date ."'
1124 p.time<='". $to_date ."'
1128 if(isset($_SESSION['searchfor_name'])) {
1130 /* check for previous conditions. if so add 'AND' */
1131 if(!empty($additional_where_cond)) {
1132 $additional_where_cond.= " AND ";
1135 if($this->dbver < 9) {
1136 $additional_where_cond.= "
1138 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1140 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1145 $additional_where_cond.= "
1147 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1149 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1155 /* limit result based on rate-search */
1156 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1158 if($this->dbver > 10) {
1160 /* check for previous conditions. if so add 'AND' */
1161 if(!empty($additional_where_cond)) {
1162 $additional_where_cond.= " AND ";
1165 $additional_where_cond.= "
1166 p.rating >= ". $_SESSION['rate_from'] ."
1168 p.rating <= ". $_SESSION['rate_to'] ."
1173 if(isset($_SESSION['sort_order'])) {
1174 $order_str = $this->get_sort_order();
1177 /* return a search result */
1178 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1180 SELECT DISTINCT pt1.photo_id
1182 INNER JOIN photo_tags pt2
1183 ON pt1.photo_id=pt2.photo_id
1187 ON pt1.photo_id=p.id
1190 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1192 if(!empty($additional_where_cond))
1193 $query_str.= "AND ". $additional_where_cond ." ";
1195 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1196 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1199 if(isset($order_str))
1200 $query_str.= $order_str;
1202 $result = $this->db->db_query($query_str);
1203 while($row = $this->db->db_fetch_object($result)) {
1204 array_push($matched_photos, $row['photo_id']);
1206 return $matched_photos;
1209 /* return according the selected tags */
1210 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1212 foreach($_SESSION['selected_tags'] as $tag)
1213 $selected.= $tag .",";
1214 $selected = substr($selected, 0, strlen($selected)-1);
1216 /* photo has to match at least on of the selected tags */
1217 if($_SESSION['tag_condition'] == 'or') {
1219 SELECT DISTINCT pt1.photo_id
1221 INNER JOIN photo_tags pt2
1222 ON pt1.photo_id=pt2.photo_id
1226 ON pt1.photo_id=p.id
1227 WHERE pt1.tag_id IN (". $selected .")
1229 if(!empty($additional_where_cond))
1230 $query_str.= "AND ". $additional_where_cond ." ";
1232 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1233 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1236 if(isset($order_str))
1237 $query_str.= $order_str;
1239 /* photo has to match all selected tags */
1240 elseif($_SESSION['tag_condition'] == 'and') {
1242 if(count($_SESSION['selected_tags']) >= 32) {
1243 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1244 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1248 /* Join together a table looking like
1250 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1252 so the query can quickly return all images matching the
1253 selected tags in an AND condition
1258 SELECT DISTINCT pt1.photo_id
1262 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1269 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1271 INNER JOIN photo_tags pt". ($i+2) ."
1272 ON pt1.photo_id=pt". ($i+2) .".photo_id
1277 ON pt1.photo_id=p.id
1279 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1280 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1282 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1285 if(!empty($additional_where_cond))
1286 $query_str.= "AND ". $additional_where_cond;
1288 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1289 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1292 if(isset($order_str))
1293 $query_str.= $order_str;
1297 $result = $this->db->db_query($query_str);
1298 while($row = $this->db->db_fetch_object($result)) {
1299 array_push($matched_photos, $row['photo_id']);
1301 return $matched_photos;
1304 /* return all available photos */
1306 SELECT DISTINCT p.id
1308 LEFT JOIN photo_tags pt
1314 if(!empty($additional_where_cond))
1315 $query_str.= "WHERE ". $additional_where_cond ." ";
1317 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1318 if(!empty($additional_where_cond))
1319 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1321 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1324 if(isset($order_str))
1325 $query_str.= $order_str;
1327 $result = $this->db->db_query($query_str);
1328 while($row = $this->db->db_fetch_object($result)) {
1329 array_push($matched_photos, $row['id']);
1331 return $matched_photos;
1333 } // getPhotoSelection()
1336 * control HTML ouput for photo index
1338 * this function provides all the necessary information
1339 * for the photo index template.
1342 public function showPhotoIndex()
1344 $photos = $this->getPhotoSelection();
1345 $current_tags = $this->getCurrentTags();
1347 $count = count($photos);
1349 /* if all thumbnails should be shown on one page */
1350 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1354 /* thumbnails should be splitted up in several pages */
1355 elseif($this->cfg->thumbs_per_page > 0) {
1357 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1361 $begin_with = $_SESSION['begin_with'];
1364 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1368 $images[$thumbs] = Array();
1369 $img_height[$thumbs] = Array();
1370 $img_width[$thumbs] = Array();
1371 $img_id[$thumbs] = Array();
1372 $img_name[$thumbs] = Array();
1373 $img_fullname[$thumbs] = Array();
1374 $img_title = Array();
1375 $img_rating = Array();
1377 for($i = $begin_with; $i < $end_with; $i++) {
1379 if(isset($photos[$i])) {
1381 $images[$thumbs] = $photos[$i];
1382 $img_id[$thumbs] = $i;
1383 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1384 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1385 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1386 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1388 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1390 if(file_exists($thumb_path)) {
1391 $info = getimagesize($thumb_path);
1392 $img_width[$thumbs] = $info[0];
1393 $img_height[$thumbs] = $info[1];
1399 // +1 for for smarty's selection iteration
1402 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1403 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1405 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1406 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1407 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1410 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1411 $this->tmpl->assign('tag_result', 1);
1414 /* do we have to display the page selector ? */
1415 if($this->cfg->thumbs_per_page != 0) {
1419 /* calculate the page switchers */
1420 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1421 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1423 if($begin_with != 0)
1424 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1425 if($end_with < $count)
1426 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1428 $photo_per_page = $this->cfg->thumbs_per_page;
1429 $last_page = ceil($count / $photo_per_page);
1431 /* get the current selected page */
1432 if($begin_with == 0) {
1436 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1443 for($i = 1; $i <= $last_page; $i++) {
1445 if($current_page == $i)
1446 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1447 elseif($current_page-1 == $i || $current_page+1 == $i)
1448 $style = "style=\"font-size: 105%;\"";
1449 elseif(($current_page-5 >= $i) && ($i != 1) ||
1450 ($current_page+5 <= $i) && ($i != $last_page))
1451 $style = "style=\"font-size: 75%;\"";
1455 $start_with = ($i*$photo_per_page)-$photo_per_page;
1457 if($this->is_user_friendly_url()) {
1458 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1461 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1463 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1467 $select.= ">". $i ."</a> ";
1469 // until 9 pages we show the selector from 1-9
1470 if($last_page <= 9) {
1471 $page_select.= $select;
1474 if($i == 1 /* first page */ ||
1475 $i == $last_page /* last page */ ||
1476 $i == $current_page /* current page */ ||
1477 $i == ceil($last_page * 0.25) /* first quater */ ||
1478 $i == ceil($last_page * 0.5) /* half */ ||
1479 $i == ceil($last_page * 0.75) /* third quater */ ||
1480 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1481 (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 */ ||
1482 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1483 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1485 $page_select.= $select;
1493 $page_select.= "......... ";
1498 /* only show the page selector if we have more then one page */
1500 $this->tmpl->assign('page_selector', $page_select);
1503 $extern_link = "index.php?mode=showpi";
1504 $rss_link = "index.php?mode=rss";
1505 if($current_tags != "") {
1506 $extern_link.= "&tags=". $current_tags;
1507 $rss_link.= "&tags=". $current_tags;
1509 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1510 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1511 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1514 $export_link = "index.php?mode=export";
1515 $slideshow_link = "index.php?mode=slideshow";
1517 $this->tmpl->assign('extern_link', $extern_link);
1518 $this->tmpl->assign('slideshow_link', $slideshow_link);
1519 $this->tmpl->assign('export_link', $export_link);
1520 $this->tmpl->assign('rss_link', $rss_link);
1521 $this->tmpl->assign('count', $count);
1522 $this->tmpl->assign('width', $this->cfg->thumb_width);
1523 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1524 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1525 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1526 $this->tmpl->assign('images', $images);
1527 $this->tmpl->assign('img_width', $img_width);
1528 $this->tmpl->assign('img_height', $img_height);
1529 $this->tmpl->assign('img_id', $img_id);
1530 $this->tmpl->assign('img_name', $img_name);
1531 $this->tmpl->assign('img_fullname', $img_fullname);
1532 $this->tmpl->assign('img_title', $img_title);
1533 $this->tmpl->assign('img_rating', $img_rating);
1534 $this->tmpl->assign('thumbs', $thumbs);
1535 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1537 $result = $this->tmpl->fetch("photo_index.tpl");
1539 /* if we are returning to photo index from an photo-view,
1540 scroll the window to the last shown photo-thumbnail.
1541 after this, unset the last_photo session variable.
1543 if(isset($_SESSION['last_photo'])) {
1544 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1545 unset($_SESSION['last_photo']);
1550 } // showPhotoIndex()
1553 * show credit template
1555 public function showCredits()
1557 $this->tmpl->assign('version', $this->cfg->version);
1558 $this->tmpl->assign('product', $this->cfg->product);
1559 $this->tmpl->assign('db_version', $this->dbver);
1560 $this->tmpl->show("credits.tpl");
1565 * create thumbnails for the requested width
1567 * this function creates image thumbnails of $orig_image
1568 * stored as $thumb_image. It will check if the image is
1569 * in a supported format, if necessary rotate the image
1570 * (based on EXIF orientation meta headers) and re-sizing.
1571 * @param string $orig_image
1572 * @param string $thumb_image
1573 * @param integer $width
1576 public function create_thumbnail($orig_image, $thumb_image, $width)
1578 if(!file_exists($orig_image)) {
1582 $mime = $this->get_mime_info($orig_image);
1584 /* check if original photo is a support image type */
1585 if(!$this->checkifImageSupported($mime))
1592 $meta = $this->get_meta_informations($orig_image);
1598 if(isset($meta['Orientation'])) {
1599 switch($meta['Orientation']) {
1600 case 1: /* top, left */
1601 /* nothing to do */ break;
1602 case 2: /* top, right */
1603 $rotate = 0; $flip_hori = true; break;
1604 case 3: /* bottom, left */
1605 $rotate = 180; break;
1606 case 4: /* bottom, right */
1607 $flip_vert = true; break;
1608 case 5: /* left side, top */
1609 $rotate = 90; $flip_vert = true; break;
1610 case 6: /* right side, top */
1611 $rotate = 90; break;
1612 case 7: /* left side, bottom */
1613 $rotate = 270; $flip_vert = true; break;
1614 case 8: /* right side, bottom */
1615 $rotate = 270; break;
1619 $src_img = @imagecreatefromjpeg($orig_image);
1625 $src_img = @imagecreatefrompng($orig_image);
1629 case 'image/x-portable-pixmap':
1631 $src_img = new Imagick($orig_image);
1632 $handler = "imagick";
1637 if(!isset($src_img) || empty($src_img)) {
1638 print "Can't load image from ". $orig_image ."\n";
1646 /* grabs the height and width */
1647 $cur_width = imagesx($src_img);
1648 $cur_height = imagesy($src_img);
1650 // If requested width is more then the actual image width,
1651 // do not generate a thumbnail, instead safe the original
1652 // as thumbnail but with lower quality. But if the image
1653 // is to heigh too, then we still have to resize it.
1654 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1655 $result = imagejpeg($src_img, $thumb_image, 75);
1656 imagedestroy($src_img);
1663 $cur_width = $src_img->getImageWidth();
1664 $cur_height = $src_img->getImageHeight();
1666 // If requested width is more then the actual image width,
1667 // do not generate a thumbnail, instead safe the original
1668 // as thumbnail but with lower quality. But if the image
1669 // is to heigh too, then we still have to resize it.
1670 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1671 $src_img->setCompressionQuality(75);
1672 $src_img->setImageFormat('jpeg');
1673 $src_img->writeImage($thumb_image);
1675 $src_img->destroy();
1682 // If the image will be rotate because EXIF orientation said so
1683 // 'virtually rotate' the image for further calculations
1684 if($rotate == 90 || $rotate == 270) {
1686 $cur_width = $cur_height;
1690 /* calculates aspect ratio */
1691 $aspect_ratio = $cur_height / $cur_width;
1694 if($aspect_ratio < 1) {
1696 $new_h = abs($new_w * $aspect_ratio);
1698 /* 'virtually' rotate the image and calculate it's ratio */
1699 $tmp_w = $cur_height;
1700 $tmp_h = $cur_width;
1701 /* now get the ratio from the 'rotated' image */
1702 $tmp_ratio = $tmp_h/$tmp_w;
1703 /* now calculate the new dimensions */
1705 $tmp_h = abs($tmp_w * $tmp_ratio);
1707 // now that we know, how high they photo should be, if it
1708 // gets rotated, use this high to scale the image
1710 $new_w = abs($new_h / $aspect_ratio);
1712 // If the image will be rotate because EXIF orientation said so
1713 // now 'virtually rotate' back the image for the image manipulation
1714 if($rotate == 90 || $rotate == 270) {
1725 /* creates new image of that size */
1726 $dst_img = imagecreatetruecolor($new_w, $new_h);
1728 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1730 /* copies resized portion of original image into new image */
1731 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1733 /* needs the image to be flipped horizontal? */
1735 $this->_debug("(FLIP)");
1736 $dst_img = $this->flipImage($dst_img, 'hori');
1738 /* needs the image to be flipped vertical? */
1740 $this->_debug("(FLIP)");
1741 $dst_img = $this->flipImage($dst_img, 'vert');
1745 $this->_debug("(ROTATE)");
1746 $dst_img = $this->rotateImage($dst_img, $rotate);
1749 /* write down new generated file */
1750 $result = imagejpeg($dst_img, $thumb_image, 75);
1752 /* free your mind */
1753 imagedestroy($dst_img);
1754 imagedestroy($src_img);
1756 if($result === false) {
1757 print "Can't write thumbnail ". $thumb_image ."\n";
1767 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1769 /* needs the image to be flipped horizontal? */
1771 $this->_debug("(FLIP)");
1772 $src_img->rotateImage(new ImagickPixel(), 90);
1773 $src_img->flipImage();
1774 $src_img->rotateImage(new ImagickPixel(), -90);
1776 /* needs the image to be flipped vertical? */
1778 $this->_debug("(FLIP)");
1779 $src_img->flipImage();
1783 $this->_debug("(ROTATE)");
1784 $src_img->rotateImage(new ImagickPixel(), $rotate);
1787 $src_img->setCompressionQuality(75);
1788 $src_img->setImageFormat('jpeg');
1790 if(!$src_img->writeImage($thumb_image)) {
1791 print "Can't write thumbnail ". $thumb_image ."\n";
1796 $src_img->destroy();
1803 } // create_thumbnail()
1806 * return all exif meta data from the file
1807 * @param string $file
1810 public function get_meta_informations($file)
1812 return exif_read_data($file);
1814 } // get_meta_informations()
1817 * create phpfspot own sqlite database
1819 * this function creates phpfspots own sqlite database
1820 * if it does not exist yet. this own is used to store
1821 * some necessary informations (md5 sum's, ...).
1823 public function check_config_table()
1825 // if the config table doesn't exist yet, create it
1826 if(!$this->cfg_db->db_check_table_exists("images")) {
1827 $this->cfg_db->db_exec("
1828 CREATE TABLE images (
1829 img_idx int primary key,
1835 } // check_config_table
1838 * Generates a thumbnail from photo idx
1840 * This function will generate JPEG thumbnails from provided F-Spot photo
1843 * 1. Check if all thumbnail generations (width) are already in place and
1845 * 2. Check if the md5sum of the original file has changed
1846 * 3. Generate the thumbnails if needed
1847 * @param integer $idx
1848 * @param integer $force
1849 * @param boolean $overwrite
1851 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1855 $resolutions = Array(
1856 $this->cfg->thumb_width,
1857 $this->cfg->photo_width,
1858 $this->cfg->mini_width,
1862 /* get details from F-Spot's database */
1863 $details = $this->get_photo_details($idx);
1865 /* calculate file MD5 sum */
1866 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1868 if(!file_exists($full_path)) {
1869 $this->_error("File ". $full_path ." does not exist\n");
1873 if(!is_readable($full_path)) {
1874 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1878 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1880 /* If Nikon NEF format, we need to treat it another way */
1881 if(isset($this->cfg->dcraw_bin) &&
1882 file_exists($this->cfg->dcraw_bin) &&
1883 is_executable($this->cfg->dcraw_bin) &&
1884 preg_match('/\.nef$/i', $details['uri'])) {
1886 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1888 /* if PPM file does not exist, let dcraw convert it from NEF */
1889 if(!file_exists($ppm_path)) {
1890 system($this->cfg->dcraw_bin ." -a ". $full_path);
1893 /* for now we handle the PPM instead of the NEF */
1894 $full_path = $ppm_path;
1898 $file_md5 = md5_file($full_path);
1901 foreach($resolutions as $resolution) {
1903 $generate_it = false;
1905 $thumb_sub_path = substr($file_md5, 0, 2);
1906 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1908 /* if thumbnail-subdirectory does not exist yet, create it */
1909 if(!file_exists(dirname($thumb_path))) {
1910 mkdir(dirname($thumb_path), 0755);
1913 /* if the thumbnail file doesn't exist, create it */
1914 if(!file_exists($thumb_path)) {
1915 $generate_it = true;
1917 /* if the file hasn't changed there is no need to regen the thumb */
1918 elseif($file_md5 != $this->getMD5($idx) || $force) {
1919 $generate_it = true;
1922 if($generate_it || $overwrite) {
1924 $this->_debug(" ". $resolution ."px");
1925 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1933 $this->_debug(" already exist");
1936 /* set the new/changed MD5 sum for the current photo */
1938 $this->setMD5($idx, $file_md5);
1941 $this->_debug("\n");
1946 * returns stored md5 sum for a specific photo
1948 * this function queries the phpfspot database for a
1949 * stored MD5 checksum of the specified photo
1950 * @param integer $idx
1951 * @return string|null
1953 public function getMD5($idx)
1955 $result = $this->cfg_db->db_query("
1958 WHERE img_idx='". $idx ."'
1964 $img = $this->cfg_db->db_fetch_object($result);
1965 return $img['img_md5'];
1970 * set MD5 sum for the specific photo
1971 * @param integer $idx
1972 * @param string $md5
1974 private function setMD5($idx, $md5)
1976 $result = $this->cfg_db->db_exec("
1977 REPLACE INTO images (img_idx, img_md5)
1978 VALUES ('". $idx ."', '". $md5 ."')
1984 * store current tag condition
1986 * this function stores the current tag condition
1987 * (AND or OR) in the users session variables
1988 * @param string $mode
1991 public function setTagCondition($mode)
1993 $_SESSION['tag_condition'] = $mode;
1997 } // setTagCondition()
2000 * invoke tag & date search
2002 * this function will return all matching tags and store
2003 * them in the session variable selected_tags. furthermore
2004 * it also handles the date search.
2005 * getPhotoSelection() will then only return the matching
2009 public function startSearch()
2012 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
2013 $from = $_POST['from'];
2015 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
2019 /* tag-name search */
2020 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2021 $searchfor_tag = $_POST['for_tag'];
2022 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2025 unset($_SESSION['searchfor_tag']);
2028 /* file-name search */
2029 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2030 $_SESSION['searchfor_name'] = $_POST['for_name'];
2033 unset($_SESSION['searchfor_name']);
2037 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2039 $_SESSION['rate_from'] = $_POST['rate_from'];
2041 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2042 $_SESSION['rate_to'] = $_POST['rate_to'];
2046 /* delete any previously set value */
2047 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2052 if(isset($from) && !empty($from))
2053 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
2055 unset($_SESSION['from_date']);
2057 if(isset($to) && !empty($to))
2058 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
2060 unset($_SESSION['to_date']);
2062 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2063 /* new search, reset the current selected tags */
2064 $_SESSION['selected_tags'] = Array();
2065 foreach($this->avail_tags as $tag) {
2066 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2067 array_push($_SESSION['selected_tags'], $tag);
2076 * updates sort order in session variable
2078 * this function is invoked by RPC and will sort the requested
2079 * sort order in the session variable.
2080 * @param string $sort_order
2083 public function updateSortOrder($order)
2085 if(isset($this->sort_orders[$order])) {
2086 $_SESSION['sort_order'] = $order;
2090 return "unkown error";
2092 } // updateSortOrder()
2097 * this function rotates the image according the
2099 * @param string $img
2100 * @param integer $degress
2103 private function rotateImage($img, $degrees)
2105 if(function_exists("imagerotate")) {
2106 $img = imagerotate($img, $degrees, 0);
2108 function imagerotate($src_img, $angle)
2110 $src_x = imagesx($src_img);
2111 $src_y = imagesy($src_img);
2112 if ($angle == 180) {
2116 elseif ($src_x <= $src_y) {
2120 elseif ($src_x >= $src_y) {
2125 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2126 imagealphablending($rotate, false);
2131 for ($y = 0; $y < ($src_y); $y++) {
2132 for ($x = 0; $x < ($src_x); $x++) {
2133 $color = imagecolorat($src_img, $x, $y);
2134 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2140 for ($y = 0; $y < ($src_y); $y++) {
2141 for ($x = 0; $x < ($src_x); $x++) {
2142 $color = imagecolorat($src_img, $x, $y);
2143 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2149 for ($y = 0; $y < ($src_y); $y++) {
2150 for ($x = 0; $x < ($src_x); $x++) {
2151 $color = imagecolorat($src_img, $x, $y);
2152 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2166 $img = imagerotate($img, $degrees);
2175 * returns flipped image
2177 * this function will return an either horizontal or
2178 * vertical flipped truecolor image.
2179 * @param string $image
2180 * @param string $mode
2183 private function flipImage($image, $mode)
2185 $w = imagesx($image);
2186 $h = imagesy($image);
2187 $flipped = imagecreatetruecolor($w, $h);
2191 for ($y = 0; $y < $h; $y++) {
2192 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2196 for ($x = 0; $x < $w; $x++) {
2197 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2207 * return all assigned tags for the specified photo
2208 * @param integer $idx
2211 private function get_photo_tags($idx)
2213 $result = $this->db->db_query("
2216 INNER JOIN photo_tags pt
2218 WHERE pt.photo_id='". $idx ."'
2223 while($row = $this->db->db_fetch_object($result)) {
2224 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2226 $tags[$row['id']] = $row['name'];
2231 } // get_photo_tags()
2234 * create on-the-fly images with text within
2235 * @param string $txt
2236 * @param string $color
2237 * @param integer $space
2238 * @param integer $font
2241 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2243 if (strlen($color) != 6)
2246 $int = hexdec($color);
2247 $h = imagefontheight($font);
2248 $fw = imagefontwidth($font);
2249 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2250 $lines = count($txt);
2251 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2252 $bg = imagecolorallocate($im, 255, 255, 255);
2253 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2256 foreach ($txt as $text) {
2257 $x = (($w - ($fw * strlen($text))) / 2);
2258 imagestring($im, $font, $x, $y, $text, $color);
2259 $y += ($h + $space);
2262 Header("Content-type: image/png");
2265 } // showTextImage()
2268 * check if all requirements are met
2271 private function check_requirements()
2273 if(!function_exists("imagecreatefromjpeg")) {
2274 print "PHP GD library extension is missing<br />\n";
2278 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2279 print "PHP SQLite3 library extension is missing<br />\n";
2283 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2284 ini_set('track_errors', 1);
2285 @include_once 'HTML/AJAX/Server.php';
2286 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2287 print "PEAR HTML_AJAX package is missing<br />\n";
2290 @include_once 'Calendar/Calendar.php';
2291 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2292 print "PEAR Calendar package is missing<br />\n";
2295 @include_once 'Console/Getopt.php';
2296 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2297 print "PEAR Console_Getopt package is missing<br />\n";
2300 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2301 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2302 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2305 ini_restore('track_errors');
2312 } // check_requirements()
2314 private function _debug($text)
2316 if(isset($this->fromcmd)) {
2323 * check if specified MIME type is supported
2324 * @param string $mime
2327 public function checkifImageSupported($mime)
2329 $supported_types = Array(
2332 "image/x-portable-pixmap",
2336 if(in_array($mime, $supported_types))
2341 } // checkifImageSupported()
2345 * @param string $text
2347 public function _error($text)
2349 switch($this->cfg->logging) {
2352 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2353 print $text ."<br />\n";
2359 error_log($text, 3, $his->cfg->log_file);
2363 $this->runtime_error = true;
2368 * output calendard input fields
2369 * @param string $mode
2372 private function get_calendar($mode)
2374 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2375 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2376 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2378 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2379 if(!isset($_SESSION[$mode .'_date']))
2380 $output.= " disabled=\"disabled\"";
2382 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2383 if(!isset($_SESSION[$mode .'_date']))
2384 $output.= " disabled=\"disabled\"";
2386 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2387 if(!isset($_SESSION[$mode .'_date']))
2388 $output.= " disabled=\"disabled\"";
2396 * output calendar matrix
2397 * @param integer $year
2398 * @param integer $month
2399 * @param integer $day
2401 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2403 if (!isset($year)) $year = date('Y');
2404 if (!isset($month)) $month = date('m');
2405 if (!isset($day)) $day = date('d');
2410 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2411 require_once CALENDAR_ROOT.'Day.php';
2414 $month = new Calendar_Month_Weekdays($year,$month);
2417 $prevStamp = $month->prevMonth(true);
2418 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2419 $nextStamp = $month->nextMonth(true);
2420 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2422 $selectedDays = array (
2423 new Calendar_Day($year,$month,$day),
2424 new Calendar_Day($year,12,25),
2427 // Build the days in the month
2428 $month->build($selectedDays);
2430 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2431 $this->tmpl->assign('prev_month', $prev);
2432 $this->tmpl->assign('next_month', $next);
2434 while ( $day = $month->fetch() ) {
2436 if(!isset($matrix[$rows]))
2437 $matrix[$rows] = Array();
2441 $dayStamp = $day->thisDay(true);
2442 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2444 // isFirst() to find start of week
2445 if ( $day->isFirst() )
2448 if ( $day->isSelected() ) {
2449 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2450 } else if ( $day->isEmpty() ) {
2451 $string.= "<td> </td>\n";
2453 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2456 // isLast() to find end of week
2457 if ( $day->isLast() )
2458 $string.= "</tr>\n";
2460 $matrix[$rows][$cols] = $string;
2470 $this->tmpl->assign('matrix', $matrix);
2471 $this->tmpl->assign('rows', $rows);
2472 $this->tmpl->show("calendar.tpl");
2474 } // get_calendar_matrix()
2477 * output export page
2478 * @param string $mode
2480 public function getExport($mode)
2482 $pictures = $this->getPhotoSelection();
2483 $current_tags = $this->getCurrentTags();
2485 foreach($pictures as $picture) {
2487 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2488 if($current_tags != "") {
2489 $orig_url.= "&tags=". $current_tags;
2491 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2492 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2495 if($this->is_user_friendly_url()) {
2496 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2499 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2505 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2506 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2510 // "[%pictureurl% %thumbnailurl%]"
2511 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2514 case 'MoinMoinList':
2515 // " * [%pictureurl% %thumbnailurl%]"
2516 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2527 public function getRSSFeed()
2529 Header("Content-type: text/xml; charset=utf-8");
2530 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2533 xmlns:media="http://search.yahoo.com/mrss/"
2534 xmlns:dc="http://purl.org/dc/elements/1.1/"
2537 <title>phpfspot</title>
2538 <description>phpfspot RSS feed</description>
2539 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2540 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2541 <generator>phpfspot</generator>
2544 $pictures = $this->getPhotoSelection();
2545 $current_tags = $this->getCurrentTags();
2547 foreach($pictures as $picture) {
2549 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2550 if($current_tags != "") {
2551 $orig_url.= "&tags=". $current_tags;
2553 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2554 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2557 $details = $this->get_photo_details($picture);
2559 if($this->is_user_friendly_url()) {
2560 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2563 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2566 $thumb_html = htmlspecialchars("
2567 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2569 ". $details['description']);
2571 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2573 /* get EXIF information if JPEG */
2574 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2575 $meta = $this->get_meta_informations($orig_path);
2578 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2582 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2583 <link><?php print htmlspecialchars($orig_url); ?></link>
2584 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2585 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2587 <?php print $thumb_html; ?>
2589 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2604 * get all selected tags
2606 * This function will return all selected tags as one string, seperated
2610 private function getCurrentTags()
2613 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2614 foreach($_SESSION['selected_tags'] as $tag)
2615 $current_tags.= $tag .",";
2616 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2618 return $current_tags;
2620 } // getCurrentTags()
2623 * return the current photo
2625 public function getCurrentPhoto()
2627 if(isset($_SESSION['current_photo'])) {
2628 print $_SESSION['current_photo'];
2630 } // getCurrentPhoto()
2633 * tells the client browser what to do
2635 * this function is getting called via AJAX by the
2636 * client browsers. it will tell them what they have
2637 * to do next. This is necessary for directly jumping
2638 * into photo index or single photo view when the are
2639 * requested with specific URLs
2642 public function whatToDo()
2644 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2646 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2647 return "showpi_tags";
2649 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2656 * return the current process-user
2659 private function getuid()
2661 if($uid = posix_getuid()) {
2662 if($user = posix_getpwuid($uid)) {
2663 return $user['name'];
2672 * returns a select-dropdown box to select photo index sort parameters
2673 * @param array $params
2674 * @param smarty $smarty
2677 public function smarty_sort_select_list($params, &$smarty)
2681 foreach($this->sort_orders as $key => $value) {
2682 $output.= "<option value=\"". $key ."\"";
2683 if($key == $_SESSION['sort_order']) {
2684 $output.= " selected=\"selected\"";
2686 $output.= ">". $value ."</option>";
2691 } // smarty_sort_select_list()
2694 * returns the currently selected sort order
2697 private function get_sort_order()
2699 switch($_SESSION['sort_order']) {
2701 return " ORDER BY p.time ASC";
2704 return " ORDER BY p.time DESC";
2707 if($this->dbver < 9) {
2708 return " ORDER BY p.name ASC";
2711 return " ORDER BY basename(p.uri) ASC";
2715 if($this->dbver < 9) {
2716 return " ORDER BY p.name DESC";
2719 return " ORDER BY basename(p.uri) DESC";
2723 return " ORDER BY t.name ASC ,p.time ASC";
2726 return " ORDER BY t.name DESC ,p.time ASC";
2729 return " ORDER BY t.name ASC, p.rating ASC";
2732 return " ORDER BY t.name DESC, p.rating DESC";
2736 } // get_sort_order()
2739 * return the next to be shown slide show image
2741 * this function returns the URL of the next image
2742 * in the slideshow sequence.
2745 public function getNextSlideShowImage()
2747 $all_photos = $this->getPhotoSelection();
2749 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2750 $_SESSION['slideshow_img'] = 0;
2752 $_SESSION['slideshow_img']++;
2754 if($this->is_user_friendly_url()) {
2755 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2758 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2760 } // getNextSlideShowImage()
2763 * return the previous to be shown slide show image
2765 * this function returns the URL of the previous image
2766 * in the slideshow sequence.
2769 public function getPrevSlideShowImage()
2771 $all_photos = $this->getPhotoSelection();
2773 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2774 $_SESSION['slideshow_img'] = 0;
2776 $_SESSION['slideshow_img']--;
2778 if($this->is_user_friendly_url()) {
2779 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2782 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2784 } // getPrevSlideShowImage()
2786 public function resetSlideShow()
2788 if(isset($_SESSION['slideshow_img']))
2789 unset($_SESSION['slideshow_img']);
2791 } // resetSlideShow()
2796 * this function will get all photos from the fspot
2797 * database and randomly return ONE entry
2799 * saddly there is yet no sqlite3 function which returns
2800 * the bulk result in array, so we have to fill up our
2804 public function get_random_photo()
2813 /* if show_tags is set, only return details for photos which
2814 are specified to be shown
2816 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2818 INNER JOIN photo_tags pt
2823 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2826 $result = $this->db->db_query($query_str);
2828 while($row = $this->db->db_fetch_object($result)) {
2829 array_push($all, $row['id']);
2832 return $all[array_rand($all)];
2834 } // get_random_photo()
2837 * get random photo tag photo
2839 * this function will get all photos tagged with the requested
2840 * tag from the fspot database and randomly return ONE entry
2842 * saddly there is yet no sqlite3 function which returns
2843 * the bulk result in array, so we have to fill up our
2847 public function get_random_tag_photo($tagidx)
2854 INNER JOIN photo_tags pt
2858 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2866 pt.tag_id LIKE '". $tagidx ."'
2869 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2872 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2876 $result = $this->db->db_query($query_str);
2878 while($row = $this->db->db_fetch_object($result)) {
2879 array_push($all, $row['id']);
2882 return $all[array_rand($all)];
2884 } // get_random_tag_photo()
2887 * validates provided date
2889 * this function validates if the provided date
2890 * contains a valid date and will return true
2892 * @param string $date_str
2895 public function isValidDate($date_str)
2897 $timestamp = strtotime($date_str);
2899 if(is_numeric($timestamp))
2907 * timestamp to string conversion
2908 * @param integer $timestamp
2911 private function ts2str($timestamp)
2913 if(!empty($timestamp) && is_numeric($timestamp))
2914 return strftime("%Y-%m-%d", $timestamp);
2919 * extract tag-names from $_GET['tags']
2920 * @param string $tags_str
2923 private function extractTags($tags_str)
2925 $not_validated = split(',', $tags_str);
2926 $validated = array();
2928 foreach($not_validated as $tag) {
2929 if(is_numeric($tag))
2930 array_push($validated, $tag);
2938 * returns the full path to a thumbnail
2939 * @param integer $width
2940 * @param integer $photo
2943 public function get_thumb_path($width, $photo)
2945 $md5 = $this->getMD5($photo);
2946 $sub_path = substr($md5, 0, 2);
2947 return $this->cfg->thumb_path
2955 } // get_thumb_path()
2958 * returns server's virtual host name
2961 private function get_server_name()
2963 return $_SERVER['SERVER_NAME'];
2964 } // get_server_name()
2967 * returns type of webprotocol which is currently used
2970 private function get_web_protocol()
2972 if(!isset($_SERVER['HTTPS']))
2976 } // get_web_protocol()
2979 * return url to this phpfspot installation
2982 private function get_phpfspot_url()
2984 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2986 } // get_phpfspot_url()
2989 * returns the number of photos which are tagged with $tag_id
2990 * @param integer $tag_id
2993 public function get_num_photos($tag_id)
2995 if($result = $this->db->db_fetchSingleRow("
2996 SELECT count(*) as number
2999 tag_id LIKE '". $tag_id ."'")) {
3001 return $result['number'];
3007 } // get_num_photos()
3010 * check file exists and is readable
3012 * returns true, if everything is ok, otherwise false
3013 * if $silent is not set, this function will output and
3015 * @param string $file
3016 * @param boolean $silent
3019 private function check_readable($file, $silent = null)
3021 if(!file_exists($file)) {
3023 print "File \"". $file ."\" does not exist.\n";
3027 if(!is_readable($file)) {
3029 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3035 } // check_readable()
3038 * check if all needed indices are present
3040 * this function checks, if some needed indices are already
3041 * present, or if not, create them on the fly. they are
3042 * necessary to speed up some queries like that one look for
3043 * all tags, when show_tags is specified in the configuration.
3045 private function checkDbIndices()
3047 $result = $this->db->db_exec("
3048 CREATE INDEX IF NOT EXISTS
3055 } // checkDbIndices()
3058 * retrive F-Spot database version
3060 * this function will return the F-Spot database version number
3061 * It is stored within the sqlite3 database in the table meta
3062 * @return string|null
3064 public function getFspotDBVersion()
3066 if($result = $this->db->db_fetchSingleRow("
3067 SELECT data as version
3070 name LIKE 'F-Spot Database Version'
3072 return $result['version'];
3076 } // getFspotDBVersion()
3079 * parse the provided URI and will returned the requested chunk
3080 * @param string $uri
3081 * @param string $mode
3084 public function parse_uri($uri, $mode)
3086 if(($components = parse_url($uri)) !== false) {
3090 return basename($components['path']);
3093 return dirname($components['path']);
3096 return $components['path'];
3106 * validate config options
3108 * this function checks if all necessary configuration options are
3109 * specified and set.
3112 private function check_config_options()
3114 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3115 $this->_error("Please set \$page_title in phpfspot_cfg");
3117 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3118 $this->_error("Please set \$base_path in phpfspot_cfg");
3120 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3121 $this->_error("Please set \$web_path in phpfspot_cfg");
3123 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3124 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3126 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3127 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3129 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3130 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3132 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3133 $this->_error("Please set \$db_access in phpfspot_cfg");
3135 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3136 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3138 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3139 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3141 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3142 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3144 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3145 $this->_error("Please set \$photo_width in phpfspot_cfg");
3147 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3148 $this->_error("Please set \$mini_width in phpfspot_cfg");
3150 if(!isset($this->cfg->thumbs_per_page))
3151 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3153 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3154 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3156 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3157 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3159 if(!isset($this->cfg->hide_tags))
3160 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3162 if(!isset($this->cfg->theme_name))
3163 $this->_error("Please set \$theme_name in phpfspot_cfg");
3165 if(!isset($this->cfg->logging))
3166 $this->_error("Please set \$logging in phpfspot_cfg");
3168 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3170 if(!isset($this->cfg->log_file))
3171 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3173 if(!is_writeable($this->cfg->log_file))
3174 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3178 /* remove trailing slash, if set */
3179 if($this->cfg->web_path == "/")
3180 $this->cfg->web_path = "";
3181 elseif(preg_match('/\/$/', $this->cfg->web_path))
3182 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3184 return $this->runtime_error;
3186 } // check_config_options()
3189 * cleanup phpfspot own database
3191 * When photos are getting delete from F-Spot, there will remain
3192 * remain some residues in phpfspot own database. This function
3193 * will try to wipe them out.
3195 public function cleanup_phpfspot_db()
3197 $to_delete = Array();
3199 $result = $this->cfg_db->db_query("
3202 ORDER BY img_idx ASC
3205 while($row = $this->cfg_db->db_fetch_object($result)) {
3206 if(!$this->db->db_fetchSingleRow("
3209 WHERE id='". $row['img_idx'] ."'")) {
3211 array_push($to_delete, $row['img_idx'], ',');
3215 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3217 $this->cfg_db->db_exec("
3219 WHERE img_idx IN (". implode($to_delete) .")
3222 } // cleanup_phpfspot_db()
3225 * return first image of the page, the $current photo
3228 * this function is used to find out the first photo of the
3229 * current page, in which the $current photo lies. this is
3230 * used to display the correct photo, when calling showPhotoIndex()
3232 * @param integer $current
3233 * @param integer $max
3236 private function getCurrentPage($current, $max)
3238 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3239 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3240 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3246 } // getCurrentPage()
3251 * this function tries to find out the correct mime-type
3252 * for the provided file.
3253 * @param string $file
3256 public function get_mime_info($file)
3258 $details = getimagesize($file);
3260 /* if getimagesize() returns empty, try at least to find out the
3263 if(empty($details) && function_exists('mime_content_type')) {
3265 // mime_content_type is marked as deprecated in the documentation,
3266 // but is it really necessary to force users to install a PECL
3268 $details['mime'] = mime_content_type($file);
3271 return $details['mime'];
3273 } // get_mime_info()
3276 * return tag-name by tag-idx
3278 * this function returns the tag-name for the requested
3279 * tag specified by tag-idx.
3280 * @param integer $idx
3283 public function get_tag_name($idx)
3285 if($result = $this->db->db_fetchSingleRow("
3289 id LIKE '". $idx ."'")) {
3291 return $result['name'];
3300 * parse user friendly url which got rewritten by the websever
3301 * @param string $request_uri
3304 private function parse_user_friendly_url($request_uri)
3306 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3308 $options = explode('/', $request_uri);
3310 switch($options[1]) {
3312 if(is_numeric($options[2])) {
3313 $this->session_cleanup();
3314 //unset($_SESSION['start_action']);
3315 //unset($_SESSION['selected_tags']);
3316 $_GET['mode'] = 'showp';
3317 return $this->showPhoto($options[2]);
3321 if(is_numeric($options[2])) {
3322 require_once "phpfspot_img.php";
3323 $img = new PHPFSPOT_IMG;
3324 if(isset($options[3]) && is_numeric($options[3]))
3325 $img->showImg($options[2], $options[3]);
3327 $img->showImg($options[2]);
3332 if(is_numeric($options[2])) {
3333 $this->session_cleanup();
3334 $_GET['tags'] = $options[2];
3335 $_SESSION['selected_tags'] = Array($options[2]);
3336 if(isset($options[3]) && is_numeric($options[3]))
3337 $_SESSION['begin_with'] = $options[3];
3338 return $this->showPhotoIndex();
3344 } // parse_user_friendly_url()
3347 * check if user-friendly-urls are enabled
3349 * this function will return true, if the config option
3350 * $user_friendly_url has been set. Otherwise false.
3353 private function is_user_friendly_url()
3355 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3360 } // is_user_friendly_url()
3365 * this function will cleanup user's session information
3367 private function session_cleanup()
3369 unset($_SESSION['begin_with']);
3370 $this->resetDateSearch();
3371 $this->resetPhotoView();
3372 $this->resetTagSearch();
3373 $this->resetNameSearch();
3374 $this->resetDateSearch();
3377 } // session_cleanup()