<?php


/*
   +----------------------------------------------------------------------+
   | Photo Class                                                          |
   | Author: Adam Goldberg                                                |
   +----------------------------------------------------------------------+
   | Copyright (c) 2008-2009 Adam J Goldberg                              |
   | This file is part of Lifebox.                                        |
   |                                                                      |
   | Lifebox is free software: you can redistribute it and/or modify      |
   | it under the terms of the GNU General Public License as published by |
   | the Free Software Foundation, either version 3 of the License, or    |
   | (at your option) any later version.                                  |
   |                                                                      |
   | Lifebox is distributed in the hope that it will be useful,           |
   | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
   | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
   | GNU General Public License for more details.                         |
   |                                                                      |
   | You should have received a copy of the GNU General Public License    |
   | along with Lifebox.  If not, see <http://www.gnu.org/licenses/>.     |
   +----------------------------------------------------------------------+
   | Index of Photos							  |
   +----------------------------------------------------------------------+
	function Photo ($string1=false,$string2=false,$cache=0,$sort=false) {
        function setInfo ($data) {
        function commit(){
	function doSearch($query,$limit=30,$offset=0,$sort=1) {
	function getSimilarMedia($album_id,$photo_id,$sort,$media='both',$limit=10) {
	function getPhotoList($string=false,$popular=false,$limit=5000,$offset=0,$sort=1) {
	function getPhotoTar($album_id) {
        function getFeaturedPhoto() {
        function getPopularPhotos($type=1,$limit=3) {
	function readPhoto($type='preview',$nocache=false,$justsize=false) {
	function getActivity($email=false,$limit=500,$offset=0) {
        function updateDimensions ($type, $width, $height ){
	function getTitleURL($photo_id,$request) {
	function generateResize($source,$destination,$width,$height,$crop=true,$quality=85,$rotate=false,$filename_extension=false) {
	function generateVideo($source,$destination,$width,$height) {
        function addToAlbum($album_id) {
        function deleteFromAlbum($album_id) {
	function moveToNewRoll($album_id) {
	function updateTagString($string) {
	function updateTagIndex() {
	function _getTags() {
	function getTagString() {
        function getTagArray() {
	function getAllTags($threshold=true) {
	function taggedPeopleEmails() {
	function addTag($tag,$x1=false,$y1=false,$x2=false,$y2=false,$email=false) {
        function deleteTag($tag) {
	function addToTagQueue($to_first,$to_last,$to_email,$photo_id,
	function flushTagQueue() {
	function autoFlushTagQueue() {
	function getAll() {
        function countPhotos($level=false) {
	function updateViews() {
	function changeLevel($level_new) {
	function getAlbums() {
        function getAllAlbumsArray() {
        function increaseComments() {
        function decreaseComments() {
	function getPhotoID($file) {
	function splitMediaFilename($filename) {
	function discoverMedia($root_directory=false,$quickscan=true,$print=false) {
	function getVideoTags($file) {
	function cleanPhotos($operation='all',$print=false) {
	function modifiedExists($file) {
        function getModifiedFile($file) {
        function getOriginalFile($file) {
        function getPathFromFile($file) {
	function _getSideCarPath($full_path) {
	function getRelativePath($full_path) {
	function _scanPhoto($full_path,$photo_md5=false,$photo_id=false) {
	function flushBuffer() {
	function _getInfo($album_title_url=false,$photo_title_url=false,$cache=0,$sort=false,$full=false) {
   +----------------------------------------------------------------------+
*/

require_once 'Album.inc.php';
require_once 'Log.inc.php';
require_once 'Text.inc.php';
require_once 'Mail.inc.php';
require_once 'File.inc.php';
require_once 'Comment.inc.php';
require_once 'XML.inc.php';

class Photo {

	var $photo_id;
	var $photo_md5;
	var $info;
	var $updated_info;
	var $new_photo;
	var $tags;
	var $album_list;

	/*****************************************************************
	 * Constructor
         *****************************************************************/
	function Photo ($string1=false,$string2=false,$cache=0,$sort=false) {
		if ($string1 AND !$string2 AND strlen($string1) == 64){
			$this->photo_id = $string1;
			$this->info = $this->_getInfo(false,false,$cache,$sort);
			$this->new_photo = false;
		} else if ($string1 AND !$string2 AND strlen($string1) == 32){
			$this->photo_md5 = $string1;
                        $this->info = $this->_getInfo(false,false,$cache,$sort);
			$this->photo_id = $this->info['photo_id'];
                        $this->new_photo = false;
		} else if ($string1 AND $string2){
			$this->info = $this->_getInfo($string1,$string2,0,$sort);
			$this->new_photo = false;
		} else {
			$this->new_photo = true;
		}

	}

	/*****************************************************************
         * setInfo
         *****************************************************************/
        function setInfo ($data) {
        global $CONFIG;
        global $ERROR;
        global $_USER;
	global $db;

		#Check Security
                if ($_USER['level'] < $CONFIG['permission_modify_photo_texts']) {
			$ERROR['security'] = 'You do not have access to modify photo information';
                        return false;
                }

                foreach ($data as $key => $value){
			$value = strip_tags($value);

			if ($key === 'title') {
			
				if (stripslashes($value) != $this->info['title']) {

					$this->updated_info['title_url'] = $this->getTitleURL($this->photo_id,$value);
				}
			}

                        $this->updated_info[$key] = strip_tags(trim($value));

                        if ($this->updated_info[$key] == 'on'){
                                $this->updated_info[$key] = 1;
                        }

                }

        }

	/*****************************************************************
         * Adds a new comment.
         *****************************************************************/
        function commit(){
        global $CONFIG;
        global $ERROR;
        global $db;

                if (!empty($ERROR)){
                        return false;
                }

                if ($this->new_photo){

			#Build the query
			foreach ($this->updated_info as $key => $value) {
				if ($key !== 'album_id' AND
				    $key !== 'album_title_url') {
				 	$query_fields .= $key.',';
				 	$query_values .= "'".addslashes($value)."',";
				}
			}

			$query_fields = substr($query_fields,0,-1);
			$query_values = substr($query_values,0,-1);

			#Check that photo_id has been set, if not, don't do anything
			if (empty($this->updated_info['photo_id'])) {
				return false;
			}

			#Insert the picture into the database
			$query = "INSERT INTO photos
				  ($query_fields)
				  VALUES($query_values)";
			$result = $db->Execute($query);

			if (!$result) {
				return false;
			}

			#If the photo was successfully inserted, insert the photo-album mapping
			$query = "INSERT INTO ".$CONFIG['tbl_album_photo_map']."
				  (album_id,photo_id)
				  VALUES('".$this->updated_info['album_id']."',
					 '".$this->updated_info['photo_id']."')";
			$result = $db->Execute($query);

			if (!$result) {
				#return false;
			}

			#If comitted, equate these two variables.
                        $this->info = $this->updated_info;
			$this->photo_id = $this->updated_info['photo_id'];

			#If this photo has been successfully committed, 
			#insert the tags.
			if (isset($this->updated_info['tag_list'])) {
				$tag_array = explode(',',$this->updated_info['tag_list']);
				foreach ($tag_array as $tag) {
					$this->addTag($tag);
				}
			}

			return true;

		} else {

                       # #Generate the current time, ip, and host
                       # $timestamp = time();
		       # this->info['timestamp_modified'] = $timestamp;

                        #Build the query.

			$set='';
                        foreach ($this->updated_info as $key=>$value){
				if (  ( $key !== 'album_id' AND
				        $key !== 'album_title_url' AND
				        $key !== 'photo_id' AND
				        $key !== 'title' AND
				        $key !== 'text' AND
				        $key !== 'tag_list' ) 
				#Fix for edit-in-place tag updates
				OR ( $key == 'tag_list' AND count($this->updated_info) == 1 ) ) {
					$set .= "$key = '".addslashes($value)."', ";
				}

				#Make sure that titles and descriptions are not
				#deleted by accident if they're updated on the site
				#and then synched from the original photo which doesn't
				#contain them.  Here we use filename as our reference
				#to determine if this update is being performed from
				#the front or the backend, since only the backend would
				#ever touch that variable.
				if (isset($this->updated_info['filename'])) {
					if ($key === 'title' AND 
					    !empty($value) AND
					    !in_array(strtolower(substr($value,0,3)),$CONFIG['default_prefix']) AND
                             		    !in_array(strtolower(substr($value,0,8)),$CONFIG['default_prefix']) AND
					    !in_array(strtolower(substr($this->info['title'],0,3)),$CONFIG['default_prefix']) AND
					    !in_array(strtolower(substr($this->info['title'],0,8)),$CONFIG['default_prefix']) ) {
						$set .= "title_url_previous = '".$this->info['title_url']."', ";
						$set .= "$key = '".addslashes($value)."', ";
					}

					if ($key === 'text' AND !empty($value)) {
                                                $set .= "$key = '".addslashes($value)."', ";
                                        }
				} else {
					if ($key === 'title') {
                                                $set .= "$key = '".$value."', ";
                                        }

                                        if ($key === 'text') {
                                                $set .= "$key = '".$value."', ";
                                        }
				}
			}

                        #strip the last comma
                        $set = substr($set,0,-2);


			$query = "UPDATE ".$CONFIG['tbl_photos']."
				  SET $set
				  WHERE photo_id = '".$this->photo_id."'";
			$result = $db->Execute($query);
			
			if (!$result) {
				return false;
			}

			# If the path has changed locations, insert this additional query
			# to make sure there's an album to photo mapping.  We also make sure
			# the updated path is set, so that this isn't triggered
			# for EVERY type of change to the photo (adding a tag)
			if (isset($this->info['path']) AND
			    isset($this->updated_info['path']) AND
			    $this->info['path'] !== $this->updated_info['path'] ) {
				$this->moveToNewRoll($this->updated_info['album_id']);
			}

			#Before we equate info with updated_info, grab the old
			#tags to calculate the difference
			if (isset($this->updated_info['tag_list'])) $tags_new = explode(',',$this->updated_info['tag_list']);
                        if (isset($this->info['tag_list'])) $tags_existing = explode(',',$this->info['tag_list']);

			#If comitted, equate these two variables.
                        $this->info = $this->updated_info;

			#If this photo has been successfully committed,
			#and if there are tags, sync the tags from the
			#file to lifebox. 
			if (!empty($this->updated_info['tag_list'])) {
				#Tags to ADD
				$to_add = array_diff($tags_new,$tags_existing);
				foreach ($to_add as $tag) {
					$this->addTag($tag);
				}

				#Tags to DELETE
				#$to_delete = array_diff($tags_existing,$tags_new);
				#foreach ($to_delete as $tag) {
				#$this->deleteTag($tag);
				#}
			}

			#LOG if a TITLE change
			if (isset($this->updated_info['title'])) {
				$log = new Log($CONFIG['changelog_photo_title']);
				if (!$log->addEvent($this->photo_id,$this->info['title'],$this->updated_info['title'])) {
				}
			}

			#LOG if a DESCRIPTION change
			if (isset($this->updated_info['text'])) {
				$log = new Log($CONFIG['changelog_photo_text']);
				if (!$log->addEvent($this->photo_id,$this->info['text'],$this->updated_info['text'])) {
				}
			}

			return true;
		}

		return false;
        }

	/*****************************************************************
	 * Perform a search
         *****************************************************************/
	function doSearch($query,$limit=30,$offset=0,$sort=1) {
	global $CONFIG;
	global $db;
	global $_USER;
	global $ERROR;

		#Check Query
		if (strlen($query) < $CONFIG['min_length_search']) {
			$ERROR['length'] = 'The query you specified is too short';
		}

		#Determine Sort
                if ($sort == 1) {
                        $order = "ORDER BY p.timestamp_original_ctime DESC, p.photo_id DESC";
                } elseif ($sort == 2) {
                        $order = "ORDER BY p.timestamp_original_ctime ASC";
                } elseif ($sort == 3) {
                        $order = "ORDER BY p.views DESC";
                } elseif ($sort == 4) {
                        $order = "ORDER BY p.views ASC";
                } elseif ($sort == 5) {
                        $order = "ORDER BY p.title ASC";
                } elseif ($sort == 6) {
                        $order = "ORDER BY p.title DESC";
		} elseif ($sort == 7) {
                        $order = "ORDER BY p.EXIF_DateTimeOriginal DESC";
		} elseif ($sort == 8) {
                        $order = "ORDER BY p.EXIF_DateTimeOriginal ASC";
		} elseif ($sort == 9) {
                        $order = "ORDER BY p.rating DESC";
		} elseif ($sort == 10) {
                        $order = "ORDER BY p.rating ASC";
                } else {
                        $order = "ORDER BY p.timestamp_original_ctime DESC, p.photo_id DESC";
                }

		if (strtolower(strpos($query,' videos'))) {
			$query = str_replace(' videos','',$query);
			$video_restriction = ' AND p.is_video = 1';
		} elseif (strtolower(strpos($query,' video'))) {
			$query = str_replace(' video','',$query);
                        $video_restriction = ' AND p.is_video = 1';
		} else {
			$video_restriction = '';
		}

		#If a COUNT was specified
		if ($limit == true AND !is_numeric($limit)) {
			$query = "SELECT COUNT(DISTINCT p.photo_id) as num
				  FROM ".$CONFIG['tbl_albums']." a,
                                ".$CONFIG['tbl_album_photo_map']." apm,
                                ".$CONFIG['tbl_photos']." p
                           WHERE p.photo_id = apm.photo_id AND
                                 apm.album_id = a.album_id AND
                                 p.level <= ".$_USER['level']." AND
				 MATCH (p.title,p.text,p.tag_list,p.comment_list,p.filename_noextension) AGAINST ('".$query."' IN BOOLEAN MODE)
				 $video_restriction";
			$row = $db->getRow($query);

			if ($row) {
				return $row['num'];
			} else {
				return false;
			}
		}

		$query = "SELECT p.filename_extension,
                                 p.photo_id,
                                 a.title_url as album_title_url,
                                 p.title_url as photo_title_url,
                                 p.timestamp_original_ctime,
                                 p.comments,
                                 p.title,
				 p.text,
				 MATCH (p.title,p.text,p.tag_list,p.comment_list,p.filename_noextension) AGAINST ('".$query."' IN BOOLEAN MODE) as score
                           FROM ".$CONFIG['tbl_albums']." a,
                                ".$CONFIG['tbl_album_photo_map']." apm,
                                ".$CONFIG['tbl_photos']." p
                           WHERE p.photo_id = apm.photo_id AND
                                 apm.album_id = a.album_id AND
                                 p.level <= ".$_USER['level']." AND
				 MATCH (p.title,p.text,p.tag_list,p.comment_list,p.filename_noextension) AGAINST ('".$query."' IN BOOLEAN MODE)
				 $video_restriction
			  GROUP BY p.photo_id
			   $order";

                 $results = $db->CacheSelectLimit($CONFIG['cache_photo_list'],$query,$limit,$offset);
                 
		if ($results) {
			$results = $results->getAll();

 	                for ($i=0; $i<count($results); $i++){
				$results[$i]['micro_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-micro.'.$results[$i]['filename_extension'];
                                $results[$i]['thumb_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-thumb.'.$results[$i]['filename_extension'];
                                $results[$i]['preview_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-preview.'.$results[$i]['filename_extension'];
                                $results[$i]['full_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-full.'.$results[$i]['filename_extension'];
                                $results[$i]['recent_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-main_recent.'.$results[$i]['filename_extension'];
                                $results[$i]['url'] = $CONFIG['full_site'].$results[$i]['album_title_url'].'/'.$results[$i]['photo_title_url'];

                                $timestamp = mktime();
                                $timestamp_expired = $timestamp - ($CONFIG['new_days'] * 24 * 60 * 60);
                                if ($results[$i]['timestamp_original_ctime'] > $timestamp_expired) {
                                        $results[$i]['is_new'] = TRUE;
                                } else {
                                        $results[$i]['is_new'] = FALSE;
                                }

                                if (in_array(strtolower($results[$i]['filename_extension']),$CONFIG['video_files'])) {
                                        $results[$i]['is_video'] = TRUE;
                                } else {
                                        $results[$i]['is_video'] = FALSE;
                                }
                         }

                        return $results;

                } else {
                        return false;
                }
		
	}

	/*****************************************************************
	 * Get similar media surrounding an item
         *****************************************************************/
	function getSimilarMedia($album_id,$photo_id,$sort,$media='both',$limit=10) {
	global $CONFIG;
	global $db;
	global $_USER;

		#Determine Sort
                if ($sort === 'random') {
			$order = "ORDER BY rand()";
		} elseif ($sort == 1) {
                        $order = "ORDER BY p.timestamp_original_ctime DESC, p.photo_id DESC";
                } elseif ($sort == 2) {
                        $order = "ORDER BY p.timestamp_original_ctime ASC";
                } elseif ($sort == 3) {
                        $order = "ORDER BY p.views DESC";
                } elseif ($sort == 4) {
                        $order = "ORDER BY p.views ASC";
                } elseif ($sort == 5) {
                        $order = "ORDER BY p.title ASC";
                } elseif ($sort == 6) {
                        $order = "ORDER BY p.title DESC";
                } elseif ($sort == 7) {
                        $order = "ORDER BY p.EXIF_DateTimeOriginal DESC";
                } elseif ($sort == 8) {
                        $order = "ORDER BY p.EXIF_DateTimeOriginal ASC";
                } elseif ($sort == 9) {
                        $order = "ORDER BY p.rating DESC";
                } elseif ($sort == 10) {
                        $order = "ORDER BY p.rating ASC";
                } else {
                        $order = "ORDER BY p.rating DESC, p.views DESC, p.EXIF_DateTimeOriginal DESC";
                }

		#Determine video restriction
		if ($media == 'video') {
                        $media_restriction = ' AND is_video = 1';
                } elseif ($media == 'photo') {
                        $media_restriction = ' AND is_video = 0';
                } else {
                        $media_restriction = '';
                }
	
		#First, determine where the currently viewed photo is
		$query = "SELECT p.filename_extension,
                                         p.photo_id,
                                         a.title_url as album_title_url,
                                         p.title_url as photo_title_url,
                                         p.title_url_previous as photo_title_url_previous,
                                         p.timestamp_original_ctime,
                                         p.comments,
                                         p.title,
                                         p.tag_list
			   FROM ".$CONFIG['tbl_albums']." a,
				".$CONFIG['tbl_album_photo_map']." apm,
				".$CONFIG['tbl_photos']." p
			  WHERE p.photo_id = apm.photo_id AND
				apm.album_id = a.album_id AND
				p.level <= ".$_USER['level']." AND
				apm.album_id = '$album_id'
				$media_restriction
				$order";
		$results = $db->CacheGetAll($CONFIG['cache_photo_list'],$query);

		#If there are no results, return false;
		if (empty($results)) {
			return Array();
		}

		$current_index = 1;
		for ($i=0; $i<count($results); $i++) {
			if ($results[$i]['photo_id'] == $photo_id) {
				$current_index = $i;
			}
		}

		#Determine median
		$median = floor($limit / 2);

		#Determine lower bound
		$lower_bound = abs($current_index - $median);
		if ($current_index <= $median) {
			$lower_bound = 1;
		}

		if ($current_index - $lower_bound > $median) {
			$difference = $current_index - $median;
			$lower_bound += $difference;
		}

		#Determine upper bound
		$upper_bound = $current_index + $median + 1;
		if ($upper_bound > count($results)) {
			$lower_bound = $lower_bound - ($upper_bound - count($results));
			$upper_bound = count($results);
		} 

		if ($current_index == count($results) - 1) {
			$lower_bound--;
		}

		if ( ($upper_bound - $lower_bound < $limit) AND ($limit < count($results))) {
			$difference = $upper_bound - $limit;
			$lower_bound = $difference;

			if ($lower_bound < 1) {
				$lower_bound = 0;
			}

			if ($upper_bound <= $limit) {
				$upper_bound += abs($difference) + 1;
			}
		} 

		#Build the return results
		for ($i=$lower_bound; $i<$upper_bound; $i++) {
			$results[$i]['micro_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-micro.'.$results[$i]['filename_extension'];
			$results[$i]['thumb_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-thumb.'.$results[$i]['filename_extension'];
			$results[$i]['preview_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-preview.'.$results[$i]['filename_extension'];
			$results[$i]['full_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-full.'.$results[$i]['filename_extension'];
			$results[$i]['recent_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-main_recent.'.$results[$i]['filename_extension'];
			$results[$i]['video_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-video.'.$results[$i]['filename_extension'];

			if (!empty($sort)) {
				$results[$i]['url'] = $CONFIG['full_site'].$results[$i]['album_title_url'].'/'.$results[$i]['photo_title_url'].'/&sort='.$sort;
			} else {
				$results[$i]['url'] = $CONFIG['full_site'].$results[$i]['album_title_url'].'/'.$results[$i]['photo_title_url'];
			}

			$timestamp = mktime();
			$timestamp_expired = $timestamp - ($CONFIG['new_days'] * 24 * 60 * 60);
			if ($results[$i]['timestamp_original_ctime'] > $timestamp_expired) {
				$results[$i]['is_new'] = TRUE;
			} else {
				$results[$i]['is_new'] = FALSE;
			}

			if (in_array(strtolower($results[$i]['filename_extension']),$CONFIG['video_files'])) {
				$results[$i]['is_video'] = TRUE;
			} else {
				$results[$i]['is_video'] = FALSE;
			}

			#Make sure no blank entries can be returned ... just in case
			if (isset($results[$i]['photo_id'])) {
				$return_array[] = $results[$i];
			}
		}
		
		if (count($return_array) > $limit AND $current_index <= $median-1) {
			array_pop($return_array);
		} elseif (count($return_array) > $limit) {
			array_shift($return_array);
		}
	
		return $return_array;

	}

	/*****************************************************************
	 * Get Latest Photos
         *****************************************************************/
	function getPhotoList($string=false,$popular=false,$limit=5000,$offset=0,$sort=1,$video=false) {
	global $CONFIG;
	global $db;
	global $_USER;

		#Determine how to identify the album, either by the
		#unique id or the title_url 
		if (strlen($string) == 32 AND !strpos($string,'-')){
			$where = "AND apm.album_id = '$string'";
		} elseif ($string === 'favorites' AND is_array($CONFIG['favorite_photos'])) {
			$where = 'AND ( ';
			foreach ($CONFIG['favorite_photos'] as $photo_id) {
				$where .= "p.photo_id = '$photo_id' OR ";
			}
			$where = substr($where,0,-3);
			$where = $where.')';	
		} elseif ($string) {
			$where = "AND a.title_url = '$string'";
		} else {
			$limit = 25;
		}

		if ($popular) {
			$popular_restriction = 'AND p.views > 0';
		} else {
			$popular_restriction = '';
		}

		if ($video == 1) {
			$video_restriction = ' AND is_video = 1';
		} elseif ($video == 2) {
			$video_restriction = ' AND is_video = 0';
		} else {
			$video_restriction = '';
		}

		$order = '';
		#Determine Sort
                if ($sort == 1) {
                        $order = "ORDER BY p.timestamp_original_ctime DESC, p.photo_id DESC";
                } elseif ($sort == 2) {
                        $order = "ORDER BY p.timestamp_original_ctime ASC";
                } elseif ($sort == 3) {
                        $order = "ORDER BY p.views DESC";
                } elseif ($sort == 4) {
                        $order = "ORDER BY p.views ASC";
                } elseif ($sort == 5) {
                        $order = "ORDER BY p.title ASC";
                } elseif ($sort == 6) {
                        $order = "ORDER BY p.title DESC";
		} elseif ($sort == 7) {
                        $order = "ORDER BY p.EXIF_DateTimeOriginal DESC";
                } elseif ($sort == 8) {
                        $order = "ORDER BY p.EXIF_DateTimeOriginal ASC";
		} elseif ($sort == 9) {
                        $order = "ORDER BY p.rating DESC";
                } elseif ($sort == 10) {
                        $order = "ORDER BY p.rating ASC";
                } else {
			$order = "ORDER BY p.rating DESC, p.views DESC, p.EXIF_DateTimeOriginal DESC";
                }

		#If a COUNT was specfied
		if ($limit == true AND !is_numeric($limit)) {
			$query = "SELECT COUNT(*) as num
                                   FROM ".$CONFIG['tbl_albums']." a,
                                        ".$CONFIG['tbl_album_photo_map']." apm,
                                       ".$CONFIG['tbl_photos']." p
                                  WHERE p.photo_id = apm.photo_id AND
                                        apm.album_id = a.album_id AND
                                        p.level <= ".$_USER['level']."
                                        $where
                                        $popular_restriction 
					$video_restriction";
			$row = $db->getRow($query);

			if ($row) {
				return $row['num'];
			} else {
				return false;
			}

		} else {
			$query = "SELECT p.filename_extension,
					 p.photo_id,
					 a.title_url as album_title_url,
	       	                         p.title_url as photo_title_url,
					 p.title_url_previous as photo_title_url_previous,
					 p.timestamp_original_ctime,
					 p.comments,
					 p.title,
					 p.tag_list
	       	                   FROM ".$CONFIG['tbl_albums']." a,
	       	                        ".$CONFIG['tbl_album_photo_map']." apm,
	                                ".$CONFIG['tbl_photos']." p
	                          WHERE p.photo_id = apm.photo_id AND
	                                apm.album_id = a.album_id AND
					p.level <= ".$_USER['level']."
					$where
					$popular_restriction 
					$video_restriction
					$order";

			if ($limit) {
				$results = $db->CacheSelectLimit($CONFIG['cache_photo_list'],$query,$limit,$offset);
				$results = $results->getAll();
			} else {
				$results = $db->CacheGetAll($CONFIG['cache_photo_list'],$query);
			}

			if ($results) {
				for ($i=0; $i<count($results); $i++){
					$results[$i]['micro_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-micro.'.$results[$i]['filename_extension'];
	       	                        $results[$i]['thumb_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-thumb.'.$results[$i]['filename_extension'];
	       	                        $results[$i]['preview_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-preview.'.$results[$i]['filename_extension'];
	       	                        $results[$i]['full_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-full.'.$results[$i]['filename_extension'];
					$results[$i]['recent_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-main_recent.'.$results[$i]['filename_extension'];
					$results[$i]['video_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-video.'.$results[$i]['filename_extension'];

					if ($sort) {
						$results[$i]['url'] = $CONFIG['full_site'].$results[$i]['album_title_url'].'/'.$results[$i]['photo_title_url'].'/&sort='.$sort;
					} else {
						$results[$i]['url'] = $CONFIG['full_site'].$results[$i]['album_title_url'].'/'.$results[$i]['photo_title_url'];
					}

					$timestamp = mktime();
					$timestamp_expired = $timestamp - ($CONFIG['new_days'] * 24 * 60 * 60);
					if ($results[$i]['timestamp_original_ctime'] > $timestamp_expired) {
						$results[$i]['is_new'] = TRUE;
					} else {
						$results[$i]['is_new'] = FALSE;
					}

					if (in_array(strtolower($results[$i]['filename_extension']),$CONFIG['video_files'])) {
						$results[$i]['is_video'] = TRUE;
					} else {
						$results[$i]['is_video'] = FALSE;
					}

				}
				return $results;
			} else {
				return false;
			}
		}
	}

	/*****************************************************************
	 * When given an album_id, this will return a .tar of all its
	 * photos for easy download 
         *****************************************************************/
	function getPhotoTar($album_id) {
	global $CONFIG;
	global $db;
	global $_USER;

		#Don't allow everyone to download
		if ($CONFIG['permission_download_all'] > $_USER['level']) {
			return false;
		}

		#Set the time limit to 30 minutes
                set_time_limit(1800);

		$query = "SELECT p.filename,
				 p.path,
				 a.title_url,
				 a.num_photos
			  FROM ".$CONFIG['tbl_photos']." p,
			       ".$CONFIG['tbl_album_photo_map']." apm,
			       ".$CONFIG['tbl_albums']." a
			  WHERE p.photo_id = apm.photo_id AND
				apm.album_id = a.album_id AND
				a.level <= ".$_USER['level']." AND
				p.level <= ".$_USER['level']." AND
				a.album_id = '$album_id'";
		$results = $db->getAll($query);

		if ($results) {
			for ($i=0; $i<count($results); $i++) {
				$modified = $CONFIG['media_path_modified'].$results[$i]['path'].$results[$i]['filename'];
				$original = $CONFIG['media_path'].$results[$i]['path'].$results[$i]['filename'];
		
				$modified_path = $CONFIG['media_path_modified'].$results[$i]['path'];
				$original_path = $CONFIG['media_path'].$results[$i]['path'];

				$file = $results[$i]['filename'];
				
				$destination = '/tmp/'.$album_id.'.tar';

				if (file_exists($modified)) {
					exec("tar --update --file $destination -C \"$modified_path\" -- $file");
				} elseif (file_exists($original)) {
					exec("tar --update --file $destination -C \"$original_path\" -- $file");
				}
			}
		
		}

			if (file_exists($destination)) {

				$filesize = filesize($destination);
				header("Content-Type: application/force-download");
	                	header("Cache-Control: max-age=1209600"); // Cache for 2 weeks
	                	header('Content-Length: '.$filesize);
			
				$filesize_mb = round($filesize/(1024*1024));

				$filename = $results[0]['title_url'].'_'.$results[0]['num_photos'].'images_'.$filesize_mb.'mb.tar';
                		$filename = strtolower($filename);

                		header("Content-Disposition: attachment; filename=".$filename);

				readfile($destination);

			}

	}

	/*****************************************************************
         * Get Image
         *****************************************************************/
        function getFeaturedPhoto() {
	global $CONFIG;
        global $db;
        global $_USER;

		$rows_exist = false;
		$i = 0;
		while (!$rows_exist AND $i < 30) {

                        $i++;
                        $timestamp = time();
                        $difference = $i * 24 * 3600 * 50;
                        $time_past = $timestamp - $difference;
			$rand = rand(0,5);

	
			#If a featured_photo_id was specified in the config
			#display that, if not ...	
			if ($CONFIG['featured_photo_id']) {
				$where = "p.photo_id = apm.photo_id AND
                                        apm.album_id = a.album_id AND
                                  p.photo_id = '".$CONFIG['featured_photo_id']."'";

			#Find a popular photo and display it.
			} else {	
				$where = "p.photo_id = apm.photo_id AND
					  apm.album_id = a.album_id AND
					  a.timestamp_created > $time_past AND
					  p.level <= ".$_USER['level']." AND
					  p.preview_width > p.preview_height AND
					  is_video = 0 AND
					  a.level <= ".$_USER['level']." AND
					  a.type = '1'";
			}

	                $query = "SELECT p.filename_extension,
	                                 p.photo_id as photo_id,
	                                 a.title_url as album_title_url,
	                                 p.title_url as photo_title_url,
					 p.*
	                          FROM ".$CONFIG['tbl_albums']." a,
	                               ".$CONFIG['tbl_album_photo_map']." apm,
	                               ".$CONFIG['tbl_photos']." p
				  WHERE $where
				  GROUP BY p.photo_id
				  ORDER BY p.rating DESC, p.comments, p.views DESC
			  	 ";

			$results = $db->cacheSelectLimit($CONFIG['cache_featured_photo'],$query,1,$rand);

			if ($results) {
				$row = Array();

				$results = $results->getAll();

				if (isset($results[0])) {
	                        	$row = $results[0];
				}

				if (count($row) > 0) {
					$rows_exist = true;
				} else {
					$rows_exist = false;
				}
			}

		}

		if ($results) {
			$row['featured_url'] = $CONFIG['full_site'].'photo/'.$row['photo_id'].'-featured.'.$row['filename_extension'];
			$row['url'] = $CONFIG['full_site'].$row['album_title_url'].'/'.$row['photo_title_url'];
			return $row;
		}  else {
			return false;
		}

        }

	/*****************************************************************
         * Get Hot Photos (Popular Ones)
         *****************************************************************/
        function getPopularPhotos($type=1,$limit=3) {
        global $CONFIG;
        global $_USER;
        global $db;
		
		#Restrict to videos if necessary
		if ($type == 1) {
			$restriction = 'AND is_video != 1';
		} elseif ($type == 2) {
			$restriction = 'AND is_video = 1';
		}

		$i=0;
		$results = false;
                while (!$results AND $i < 30) {
                        
                        $i = $i + 1;
                        $timestamp = time();
                        $difference = $i * 2 * 7 * 24 * 450;
                        $time_past = $timestamp - $difference;
			$big_limit = $limit * 10;
			$rand = rand(0,10);

                        $query = "( SELECT p.*,
                                         a.title_url as album_title_url,
                                         p.title_url as photo_title_url,
                                         p.title as photo_title,
                                         a.title as album_title,
					 MATCH (p.title,p.text,p.tag_list,p.comment_list,p.filename_noextension) AGAINST ('".$_USER['first'].' '.$_USER['last']."') as score
                                  FROM photos p, 
                                       album_photo_map apm, 
                                       albums a 
                                  WHERE p.photo_id = apm.photo_id AND 
                                        apm.album_id = a.album_id AND 
                                        p.timestamp_last_view > $time_past AND
					p.level <= ".$_USER['level']."
					$restriction
                                  GROUP BY p.photo_id
                                  ORDER BY score DESC, p.views DESC, a.timestamp_created DESC LIMIT $big_limit ) 
				    ORDER BY rand()";
                        $results = $db->cacheSelectLimit($CONFIG['cache_popular_photos'],$query,$limit,0);
                }

                if (!$results) {
                        return false;
                }

                $results = $results->getAll();

                for ($i=0; $i<count($results); $i++) {
                                $results[$i]['recent_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-main_recent.'.$results[$i]['filename_extension'];
				$results[$i]['preview_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-preview.'.$results[$i]['filename_extension'];
				$results[$i]['full_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-full.'.$results[$i]['filename_extension'];
                                $results[$i]['url'] = $CONFIG['full_site'].$results[$i]['album_title_url'].'/'.$results[$i]['photo_title_url'];
                                $results[$i]['video_url'] = $CONFIG['full_site'].'photo/'.$results[$i]['photo_id'].'-video.'.$results[$i]['filename_extension'];
                }

                return $results;
        }

	
	/*****************************************************************
         * Get Image
         *****************************************************************/
	function readPhoto($type='preview',$nocache=false,$justsize=false) {
	global $CONFIG;
	global $_USER;

		switch ($type) {
			case 'main_album':
				$width = $CONFIG['main_album_width'];
                                $height = $CONFIG['main_album_height'];
                                $crop = true;
                                break;
			case 'main_event':
                                $width = $CONFIG['main_event_width'];
                                $height = $CONFIG['main_event_height'];
                                $crop = true;
                                break;
			case 'featured':
				$width = $CONFIG['main_featured_width'];
				$height = $CONFIG['main_featured_height'];
				$crop = true;
				break;
			case 'main_recent':
				$width = $CONFIG['main_recent_width'];
				$height = $CONFIG['main_recent_height'];
				$crop = true;
				break;
			case 'album':
				$width = $CONFIG['album_width'];
                                $height = $CONFIG['album_height'];
                                $crop = true;
                                break;
                        case 'thumb':   
                                $width = $CONFIG['thumb_width'];
                                $height = $CONFIG['thumb_height'];
				$crop = true;
                                break;
                        case 'preview':   
                                $width = $CONFIG['preview_width'];
                                $height = $CONFIG['preview_height'];
				$crop = false;
                                break;
                        case 'full':   
                                $width = $CONFIG['full_width'];
                                $height = $CONFIG['full_height'];
				$crop = false;
                                break;
			case 'roll':
				$width = $CONFIG['roll_width'];
				$height = $CONFIG['roll_height'];
				$crop = true;
				break;
			case 'micro':
				$width = $CONFIG['micro_width'];
				$height = $CONFIG['micro_height'];
				$crop = true;
				break;
			default:
				$width = $CONFIG['preview_width'];
                                $height = $CONFIG['preview_height'];
				$crop = false;
                                break;
		}

		#Check Security
		if ($type === 'download' AND $_USER['level'] < $CONFIG['permission_download_original']) {
			$ERROR['security'] = 'You do not have access to download original files';
			return false;
		}

		#Set appropriate paths
		$cache_path = $CONFIG['lifebox_cache'].$this->info['path'];
		$cache_file = $cache_path.'/'.$width.'x'.$height.'-'.$this->info['filename'];
		$file_original = $this->getOriginalFile($this->info['path'].$this->info['filename']);
		$file_modified = $this->getModifiedFile($this->info['path'].$this->info['filename']);

		#If we're looking for a cached file, and if this is not a request to download
		#check if the cache directory exists it not, make it.  Then check if a 
		#modified version of the file exists.  Then, generate the thumbnail/cached
		#version if necessary
		if ( ($type !== 'download' AND !file_exists($cache_file) ) OR $nocache) {
			if (!file_exists($cache_path)) {
				mkdir($cache_path,0755,true);
			}

			#Check on the fly if a modified version exists.  If it does,
			#use it.  If not, use the original version.
			if ($this->modifiedExists($this->info['path'].$this->info['filename']) AND !in_array(strtolower($this->info['filename_extension']),$CONFIG['video_files'])) {
				$source = $file_modified;
			} else {
				$source = $file_original;
			}

			if (isset($this->info['EXIF_Orientation'])) {
				$exif_orientation = $this->info['EXIF_Orientation'];
			} else {
				$exif_orientation = false;
			}

			$this->generateResize($source,$cache_file,$width,$height,$crop,85,$exif_orientation,$this->info['filename_extension']);

			#Update the database
			list($detected_width, $detected_height, $detected_type, $detected_attr) = getimagesize($cache_file);
			$this->updateDimensions($type,$detected_width,$detected_height);

		#If this is for a streaming video, make sure the video has been compressed
		#if not, compress it for download
		} elseif ($type === 'video' AND in_array(strtolower($this->info['filename_extension']),$CONFIG['video_files'])) {

			if (isset($this->info['EXIF_Orientation'])) {
                                $exif_orientation = $this->info['EXIF_Orientation'];
                        } else {
                                $exif_orientation = false;
                        }

			#First, ensure the thumbnail has been generated.  We use this thumbnail to determine
			#the height and width of the video.  This height and width are crucial to make sure 
			#the video is generated to an optimized size with the correct ratio.
			$source = $file_original;
			if (!file_exists($cache_file) OR $nocache) {
				$this->generateResize($source,$cache_file,$width,$height,$crop,85,$exif_orientation,$this->info['filename_extension']);
			}
                        
			#Update the database
                        list($detected_width, $detected_height, $detected_type, $detected_attr) = getimagesize($cache_file);
                        $this->updateDimensions($type,$detected_width,$detected_height);

			#Now, proceed to the video information
                        $cache_file = $cache_path.'/'.$this->info['filename_noextension'].'.mov';

			#First, check if the FLV has been created
                        if (!file_exists($cache_file)) {
				if (!file_exists($cache_path)) {
                                	mkdir($cache_path,0755,true);
                        	}	
                                
			        $source = $file_original;

                                $this->generateVideo($source,$cache_file,$detected_width, $detected_height);

                        }
		}

		#if we only want the size of the requested file, don't return
		#anything other than the size
		if ($justsize) {

			#If a download is requested, return the size of
			#the original file since that's what we'll be
			#downloading
			if ($type === 'download') {
				return filesize($file_original);

			#Otherwise, return the filesize of the cached
			#file
			} else {
				return filesize($cache_file);
			}

		#If we want to download the original file, get the data
		#for the original file
		} else if ($type === 'download') {
			$data = file_get_contents($file_original);

		#For everything else, get the cached version of the file
		} else {
			$data = file_get_contents($cache_file);
		}

		#if there's something to return, return it.  If not,
		#return false
		if ($data){
			return $data;
		} else {
			return false;
		}
	}

	/*****************************************************************
	 * Get activity for a user
         *****************************************************************/
	function getActivity($email=false,$limit=500,$offset=0) {
	global $CONFIG;
	global $db;

		if ($email) {
			$where = "WHERE email = '$email'";
		} else {
			$where = "WHERE email != '".$CONFIG['admin_email']."'";
		}

		$query = "SELECT v.*,
				 a.title as album_title,
				 a.title_url as album_title_url,
				 p.title as photo_title,
				 p.title_url as photo_url,
			 	 p.comments as comments,
				 p.views as views
			  FROM ".$CONFIG['tbl_views']." v
			  LEFT JOIN ".$CONFIG['tbl_photos']." p ON (v.photo_id = p.photo_id)
			  LEFT JOIN ".$CONFIG['tbl_albums']." a ON (v.album_id = a.album_id)
			  $where
			  ORDER BY v.timestamp_created DESC";
		$results = $db->SelectLimit($query,$limit,0);

		if ($results) {
			$results = $results->getAll();	
			for ($i=0; $i<count($results); $i++) {
				$results[$i]['url_nosite'] = str_replace($CONFIG['full_site'],'',$results[$i]['url']);
			}

			return $results;
		} else {
			return false;
		}
	}

	/*****************************************************************
         * Updates thumbnail dimensions
         *****************************************************************/
        function updateDimensions ($type, $width, $height ){
	global $db;
	global $CONFIG;

		$query = "UPDATE ".$CONFIG['tbl_photos']."
			  SET ".$type."_width = $width,
			      ".$type."_height = $height,
			      ".$type."_generated = 1
			  WHERE photo_id = '".$this->photo_id."'";
		$result = $db->Execute($query);
		if ($result) {
			return true;
		} else {
			return false;
		}
	}

	/*****************************************************************
	 * Given a title, return the best title URL that's clean and
	 * availalbe
         *****************************************************************/
	function getTitleURL($photo_id,$request) {
	global $CONFIG;
	global $db;

		#Clean up the title
		$title_url = Text::generateCleanTitle($request);
		
		#Set the number of titles ro something greater than 0
		$num_title = 1;
		while ($num_title != 0) {

			#Check that this title_url is unique and valid
			#First, get all albums this image is a part of
			$query = "SELECT album_id
				  FROM ".$CONFIG['tbl_album_photo_map']." apm
				  WHERE photo_id = '".$photo_id."'";
			$results = $db->getAll($query);

			$or_string = '';
			for ($i=0; $i<count($results); $i++) {
				$or_string .= "a.album_id = '".$results[$i]['album_id']."' OR ";
			}

			#Strip the last OR
			$or_string = substr($or_string,0,-3);

			#Count the number of existing unique title urls
			$query = "SELECT COUNT(*) as num
				  FROM ".$CONFIG['tbl_albums']." a,
				       ".$CONFIG['tbl_album_photo_map']." apm,
				       ".$CONFIG['tbl_photos']." p
				  WHERE ( p.title_url = '$title_url' OR p.title_url_previous = '$title_url' ) AND
					($or_string) AND
					p.photo_id = apm.photo_id AND
					p.photo_id != '$photo_id' AND
					apm.album_id = a.album_id";
			$row = $db->getRow($query);

			#Set the while look with the number of matching rows found
			$num_title = $row['num'];
			if ($row['num'] > 0) {
				$title_counter++;

				if ($title_counter == 1) {
					$title_counter++;
				}

				$title_url = $title_url . '_' . $title_counter;
			} else {
				$num_title = 0;
			}
		}

		return $title_url;
	}

	/*****************************************************************
	 * Resize
         *****************************************************************/
	function generateResize($source,$destination,$width,$height,$crop=true,$quality=85,$rotate=false,$filename_extension=false) {
	global $CONFIG;

		#Remove double slashes
                $source = str_replace('//','/',$source);
                $destination = str_replace('//','/',$destination);

		#Determine height and width
		list($original_width, $original_height, $type, $attr) = getimagesize($source);
		list($modified_width,$modified_height,$type,$attr) = getimagesize($this->getModifiedFile($source));

                if ($original_width > $original_height AND $original_height != 0){
  	              $factor = round($original_width / $original_height,2);
                 } else if ($original_width != 0) {
                      $factor = round($original_height / $original_width,2);
                 }

		if ($factor == 0) {
			$factor = 2;
		}
		#Make tweaks for portraits
		if ($this->modifiedExists($source)) {
			if ($rotate == 6 AND $modified_width > $modified_height) {
				#The actual rotation, 90 clockwise
				$rotate_string = "-rotate 90";
			} elseif ($rotate == 8 AND $modified_width > $modified_height) {
				$rotate_string = "-rotate 270";
			} else {
				$rotate_string = '';
			}
		} else {
			if ($rotate == 6 AND $original_width > $original_height) {
                                #The actual rotation, 90 clockwise
                                $rotate_string = "-rotate 90";
                        } elseif ($rotate == 8 AND $original_width > $original_height) {
                                $rotate_string = "-rotate 270";
                        } else {
                                $rotate_string = '';
                        }
		}

		#If we're rotating ...
		if ($rotate_string) {
       
			#Swap the width and height
                        $width_bigger = round($height*$factor*2);
                        $height_bigger = round($width*$factor*2);

                        #Swap the width and height if a portrait
                        $temp = $width;
                        $width = $height;
                        $height = $temp;

		} else {
		
			#Standard for landscapes
                        $width_bigger = round($width*$factor);
                        $height_bigger = round($height*$factor);

                }

		if ($crop) {
			### We intentionally specify the width as the height
                        # because we only care that the width is the same
                        #Size
                        if ($width_bigger AND $height_bigger AND $width < 300) {
                                $size = "-size $width"."x"."$height";
                        }

                        #Thumbnail
                        if ($width_bigger > 0 AND $width < 300) {
                                $thumbnail = "-thumbnail $width_bigger"."x"."$width_bigger";
                        }
		} else {
			$width_bigger = $width;
		}

		#If a video, do a special thumbnail generation
		if (in_array(strtolower($filename_extension),$CONFIG['video_files'])){

			$exec_thumb = "".$CONFIG['nice_path']." -19 ".$CONFIG['ffmpeg_path']." -i \"".$source."\" -y -ss 1 -t 0.01 -f mjpeg - | \
				       ".$CONFIG['nice_path']." -19 ".$CONFIG['convert_path']." - $size $thumbnail -resize '$width_bigger' -gravity center \
				       -crop $width"."x"."$height+0+0 +repage -format jpg -quality $quality \"jpg:$destination\"";

		#If Not a video
		} else {

			if ($crop) {
				$exec_thumb = "".$CONFIG['nice_path']." -19 ".$CONFIG['convert_path']." $size \"".$source."[0]\" $thumbnail -resize $width_bigger"."x"."$width_bigger -gravity center \
			       	       -crop $width"."x"."$height+0+0 +repage -format jpg -quality $quality $rotate_string \"jpg:$destination\"";
			} else {
                                $exec_thumb = "".$CONFIG['nice_path']." -19 ".$CONFIG['convert_path']." $size \"".$source."[0]\" -resize $width"."x"."$height -quality $quality $rotate_string \"jpg:$destination\"";
			}

		}

		exec("echo $exec_thumb >> /tmp/convert.log");
	        if (exec($exec_thumb)) {
			return true;	
		} else {
			return false;
		}
	}

	/*****************************************************************
	 * Generate FLV from other video formats
         *****************************************************************/
	function generateVideo($source,$destination,$width,$height) {
	global $CONFIG;

		#Remove double slashes
		$source = str_replace('//','/',$source);
		$destination = str_replace('//','/',$destination);

		#Determine proper ratio, height, and width
		if ($height == 0 ) {
			return false;
		}
		$ratio = round($width / $height,2);

		if ($ratio == '1.78') {
			$size = '-s 960x544 -aspect 16:9';
		} else {
			$size = '-s 960x720 -aspect 4:3';
		}

		#Generate the video
		$temp_destination = $destination.'-temp.mov';
		$lock = $destination.'.lock';
	
		if (isset($CONFIG['ffmpeg_threads'])) {
			$threads = '-threads '.$CONFIG['ffmpeg_threads'];
		} else {
			$threads = '';
		}
	
		if (!file_exists($lock)) {
			touch($lock);

			$exec_command = "cd ".$CONFIG['lifebox_cache']." && \
					 ".$CONFIG['ffmpeg_path']." $threads -y \
					 -i \"$source\" ".$CONFIG['ffmpeg_convert_flags']." $size -bufsize 2M \
					 -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 \
					 -flags2 +mixed_refs -subq 6 -trellis 0 -refs 5 -bf 5 -b_strategy 1 \
					 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 \
					 -i_qfactor 0.71 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.71 \
					 -qmin 10 -qmax 51 -qdiff 4 -deinterlace \"$destination\"";
			exec($exec_command);

			#Attempt a qt-faststart
			$exec_command = $CONFIG['qt_faststart_path']." \"$destination\" \"$temp_destination\"";
			exec($exec_command);

			#If the qt-faststart was successful, move it to the destination
			if (file_exists($temp_destination)) {
				unlink ($destination);
				rename ($temp_destination,$destination);
			}

			#Remove the lock
			unlink($lock);
		}

		return true;
	}

	/*****************************************************************
	 * Add a photo to an album
         *****************************************************************/
        function addToAlbum($album_id) {
	global $CONFIG;
	global $db;
	global $_USER;
	global $ERROR;

		$timestamp = time();

		#Check Security
		if ($_USER['level'] < $CONFIG['permission_album_association']) {
			$ERROR['security'] = 'You do not have this access';		
			return false;
		}	

		#First Check that no such title_url exists in the destination album
		$query = "SELECT COUNT(*) as num
			  FROM ".$CONFIG['tbl_photos']." p,
			       ".$CONFIG['tbl_album_photo_map']." apm,
		               ".$CONFIG['tbl_albums']." a
			  WHERE p.photo_id = apm.photo_id AND
				apm.album_id = a.album_id AND
				a.album_id = '$album_id' AND
				p.title_url = '".$this->info['title_url']."'";
		$row = $db->getRow($query);

		if ($row['num'] > 0) {
			$ERROR['title_url_exists'] = 'A photo with this name already exists in the destination album';
			return false;
		}

		#Insert the log entry
		$log = new Log($CONFIG['changelog_album_association']);
                if (!$log->addEvent($this->photo_id,NULL,$album_id)) {
			return false;
		}

		$query = "INSERT INTO ".$CONFIG['tbl_album_photo_map']."
			  (photo_id,album_id)
			  VALUES ('".$this->photo_id."',
				  '".$album_id."')";
		$result = $db->Execute($query);

		if (!$result) {
			return false;
		}

		#Update Album Count ################################
		$query = "SELECT COUNT(*) as num
			  FROM ".$CONFIG['tbl_album_photo_map']."
			  WHERE album_id = '$album_id'";
		$row = $db->getRow($query);

		if (!$row) {
			return false;
		}

		#,timestamp_modified = $timestamp commented out

		$num_photos = $row['num'] + 1;
		$query = "UPDATE ".$CONFIG['tbl_albums']."
			  SET num_photos = $num_photos,
			      timestamp_modified = $timestamp
			  WHERE album_id = '".$album_id."'";

		$result = $db->Execute($query);

		if (!$result) {
			return false;
		}
		####################################################


		#Update Level Cache ################################
                $query = "SELECT MIN(level) as min_level
                          FROM ".$CONFIG['tbl_photos']." p, 
                               ".$CONFIG['tbl_album_photo_map']." apm, 
                               ".$CONFIG['tbl_albums']." a 
                          WHERE a.album_id = apm.album_id 
                               AND apm.photo_id = p.photo_id 
                               AND a.album_id = '$album_id'
                          GROUP BY a.album_id;";
                $row = $db->GetRow($query);

                $query = "UPDATE ".$CONFIG['tbl_albums']."
                          SET min_level = '".$row['min_level']."'
                          WHERE album_id = '".$album_id."'";
                $row = $db->Execute($query);

                if (!$row) {
                        return false;
                }
                ####################################################

		return true;
		
        }
	
	/*****************************************************************
         * Deletes a photo from an album
         *****************************************************************/
        function deleteFromAlbum($album_id) {
        global $CONFIG;
        global $db;
        global $_USER;
        global $ERROR;

                #Check Security
                if ($_USER['level'] < $CONFIG['permission_album_association']) {
                        $ERROR['security'] = 'You do not have this access';             
                        return false;
                }       

		#Make sure this photo has at LEAST one other album to prevent
		#album-less photos.  The count has to be greater or equal
		#to one because each photo is part of a roll (which is
		#a type of album).
		$query = "SELECT COUNT(*) as num
			  FROM ".$CONFIG['tbl_album_photo_map']."
			  WHERE photo_id = '".$this->photo_id."' AND
				album_id != '$album_id'";
		$row = $db->getRow($query);

		if ($row['num'] < 0) {
			return false;
		}

		#Insert the log entry
                $log = new Log($CONFIG['changelog_album_association']);
                if (!$log->addEvent($this->photo_id,$album_id,NULL)) {
                        return false;
                }

                $query = "DELETE FROM ".$CONFIG['tbl_album_photo_map']."
			  WHERE photo_id = '$this->photo_id' AND
				album_id = '$album_id'";
                $result = $db->Execute($query);

                if (!$result) {
                        return false;
                }
               
		#Update Album Count ################################
                $query = "SELECT COUNT(*) as num
                          FROM ".$CONFIG['tbl_album_photo_map']."
                          WHERE album_id = '$album_id'";
                $row = $db->getRow($query);

                if (!$row) {
                        return false;
                }

                $num_photos = $row['num'] - 1;
                $query = "UPDATE ".$CONFIG['tbl_albums']."
                          SET num_photos = $num_photos
                          WHERE album_id = '".$album_id."'";

                $result = $db->Execute($query);

                if (!$result) {
                        return false;
                }
                ####################################################


                #Update Level Cache ################################
                $query = "SELECT MIN(level) as min_level
                          FROM ".$CONFIG['tbl_photos']." p, 
                               ".$CONFIG['tbl_album_photo_map']." apm, 
                               ".$CONFIG['tbl_albums']." a 
                          WHERE a.album_id = apm.album_id 
                               AND apm.photo_id = p.photo_id 
                               AND a.album_id = '$album_id'
                          GROUP BY a.album_id;";
                $row = $db->GetRow($query);

                $query = "UPDATE ".$CONFIG['tbl_albums']."
                          SET min_level = '".$row['min_level']."'
                          WHERE album_id = '".$album_id."'";
                $row = $db->Execute($query);

                if (!$row) {
                        return false;
                }
                ####################################################

                return true;
 
        }

	/*****************************************************************
	 * Move a photo to a new roll in the database (does not touch
	 * physical location of the file so that must already be moved)
	 * and remove all album-photo roll maps that are no longer active
         *****************************************************************/
	function moveToNewRoll($album_id) {
	global $CONFIG;
	global $db;

		#If no album_id was specified, return false.
		if (empty($album_id)) {
			return false;
		}

		#First, insert the new roll album-map association
		$query = "INSERT INTO ".$CONFIG['tbl_album_photo_map']."
			  (album_id,photo_id)
			  VALUES('".$album_id."',
				 '".$this->photo_id."')";
		$result = $db->Execute($query);

		# Select all rolls for which the above photo is a part
		# of EXCEPT the above roll, which we want to keep, and
		# delete them
		$query = "SELECT apm.album_id
			  FROM ".$CONFIG['tbl_albums']." a,
			       ".$CONFIG['tbl_album_photo_map']." apm
			  WHERE a.album_id = apm.album_id AND
				a.type = '1' AND
				apm.photo_id = '".$this->photo_id."' AND
				apm.album_id != '$album_id'";
		$results = $db->getAll($query);

		if (!$results) {
			return false;
		}

		# Now, delete all other results, should just be one
		# But you never know.
		foreach ($results as $row) {
			$query = "DELETE FROM ".$CONFIG['tbl_album_photo_map']."
				  WHERE album_id = '".$row['album_id']."' AND
					photo_id = '".$this->photo_id."'";
			$result = $db->Execute($query);
		}

		return true;		
	}

	/*****************************************************************
	 * Update Tags
         *****************************************************************/
	function updateTagString($string) {
	global $CONFIG;
	global $db;

		#Declare variables in case they're empty !
		$tags_existing = Array();
		$tags_new = Array();

		#Populate the tags if none have been set
		if (!isset($this->tags)) {
			$this->_getTags();
		}

		#Build simple array of existing tags
		$old_tags = '';
		for ($i=0; $i<count($this->tags); $i++) {
			$tags_existing[] = $this->tags[$i]['title'];
			$old_tags .= $this->tags[$i]['title'].', ';
		}

		#Strip the last comma from old_tags
		$old_tags = substr($old_tags,0,-2);

		#Build simple array of new tags
		$tags_new = explode(',',$string);
		for ($i=0; $i<count($tags_new); $i++) {
			$tags_new[$i] = stripslashes(trim($tags_new[$i]));
		}

		#Tags to ADD
		$to_add = array_diff($tags_new,$tags_existing);
	
		foreach ($to_add as $tag) {
                        $this->addTag($tag);
                }
	
		#Tags to DELETE
		$to_delete = array_diff($tags_existing,$tags_new);

		foreach ($to_delete as $tag) {
			$this->deleteTag($tag);
		}

		$tag_list = $this->updateTagIndex();	

		#LOG CHANGE
                $log = new Log($CONFIG['changelog_photo_tags']);
                if (!$log->addEvent($this->photo_id,$old_tags,$tag_list)) {
                	return false;
                }

		return true;
	}

	/*****************************************************************
	 * Updates a text string of existing tags in the photos table to aid
	 * in searching
         *****************************************************************/
	function updateTagIndex() {
	global $CONFIG;
	global $db;

		#Populate the new tags
                $this->_getTags();

		$tag_list = '';
                foreach ($this->tags as $row) {
                        $tag_list .= $row['title'].', ';
                }

                #Strip the last space
                $tag_list = substr($tag_list,0,-2);
                $tag_list = addslashes($tag_list);

                #Update the TAGS list for searches
                $query = "UPDATE ".$CONFIG['tbl_photos']."
                          SET tag_list = '$tag_list'
                          WHERE photo_id = '".$this->photo_id."'";
                $result = $db->Execute($query);

                if (!$result) {
                        return false;
                }
		
		return $tag_list;	

	}

	/*****************************************************************
         * Reads Tags
         *****************************************************************/
	function _getTags() {
	global $CONFIG;
	global $db;

		$query = "SELECT *
			  FROM ".$CONFIG['tbl_tags']." t,
			       ".$CONFIG['tbl_tag_photo_map']." tpm
			  WHERE t.tag_id = tpm.tag_id AND
				tpm.photo_id = '".$this->photo_id."'
			  ORDER BY t.title ASC";
		$results = $db->getAll($query);

		$this->tags = $results;
	}

	/*****************************************************************
	 * Returns tags
         *****************************************************************/
	function getTagString() {
	
		#Populate the tags if none have been set
                if (!isset($this->tags)) {
                        $this->_getTags();
                }

		$tag_string = '';
		foreach ($this->tags as $row) {
			$tag_string .= $row['title'].', ';
		}

		#Strip the last comma
		$tag_string = substr($tag_string,0,-2);

		return $tag_string;

	}

	/*****************************************************************
         * Returns tags
         *****************************************************************/
        function getTagArray() {

                #Populate the tags if none have been set
                if (!isset($this->tags)) {
                        $this->_getTags();
                }

                return $this->tags;

        }

	/*****************************************************************
	 * Return an array of all tags
         *****************************************************************/
	function getAllTags($threshold=true) {
	global $CONFIG;
	global $db;

		if ($threshold) {
			$restriction = "WHERE t.count > ".$CONFIG['tag_threshold'];
		}

		$query = "SELECT *
			  FROM ".$CONFIG['tbl_tags']." t
			  $restriction
			  ORDER BY t.title ASC";
		$results = $db->getAll($query);

		if ($results) {
			return $results;
		} else {
			return false;
		}
	}

	/*****************************************************************
	 * Looks up all the names in tags specified for a photo and for
	 * each of the names, it looks up the e-mail address from the access
	 * table
         *****************************************************************/
	function taggedPeopleEmails() {
	global $CONFIG;
	global $db;
	global $_USER;

		$query = "SELECT title
			  FROM ".$CONFIG['tbl_tags']." t,
			       ".$CONFIG['tbl_tag_photo_map']." tpm
			  WHERE photo_id ='$this->photo_id' AND
				t.tag_id = tpm.tag_id";
		$results = $db->getAll($query);

		foreach ($results as $tag) {
			$name_array = Text::getNameArray($tag['title']);
                        if (isset($name_array['first_name'])) $first = $name_array['first_name'];
                        if (isset($name_array['last_name'])) $last = $name_array['last_name'];

                        $query = "SELECT email
                                  FROM ".$CONFIG['tbl_access']."
                                  WHERE first = '$first' AND
                                        last = '$last'";
                        $row = $db->getRow($query);

			if ($row) {
				$emails[$row['email']] = $row['email'];
			}
		}

		#Build a simple array for return
		if (count($emails) > 0) {
			foreach ($emails as $key=>$value) {
				if (!is_numeric($key)) {
					$emails[$key] = $value;
				}
			}
		}
		#Return the emails
		return $emails;
	}

	/*****************************************************************
	 * Adds a tag to the system
         *****************************************************************/
	function addTag($tag,$x1=false,$y1=false,$x2=false,$y2=false,$email=false) {
	global $CONFIG;
	global $db;
	global $_USER;

		if ($_USER['level'] < $CONFIG['permission_add_photo_tags']) {
                        return false;
                }

		if (!is_string($tag) OR empty($tag)) {
			return false;
		}

		$timestamp = time();

		$tag = strip_tags($tag);
		$tag = trim($tag);

		#Strip slashes then put them back.
		$tag = stripslashes($tag);
		$tag = addslashes($tag);

		#First, see if the tag exists, ad if it does, get the tag_id
		$query = "SELECT tag_id
			  FROM ".$CONFIG['tbl_tags']."
			  WHERE title = '".$tag."'";
		$row = $db->getRow($query);

		#If the tag exists, use the existing tag_id ... if not, make it
		if (isset($row['tag_id'])) {
			$tag_id = $row['tag_id'];

			#First, do a SELECT to see if the tag association already
			#exists, we use this information to determine if we need
			#to insert a new tag, or update a tag with xy coordinates
			$query = "SELECT COUNT(*) as num
				  FROM ".$CONFIG['tbl_tag_photo_map']."
				  WHERE tag_id = '".$tag_id."' AND
					photo_id = '".$this->photo_id."'";
			$row = $db->getRow($query);

			if ($row['num'] > 0) {
				$tag_associated = true;
			} else {
				$tag_associated = false;
			} 

		} else {
	
			$tag_associated = false;
	
			#Generate a tag_id
			$tag_id = md5($tag.$timestamp);
		
			#Generate the clean tag url title
			$title_url = Text::generateCleanTitle($tag);
 
			$query = "INSERT INTO ".$CONFIG['tbl_tags']."
				  (tag_id,title,title_url,timestamp_created)
				  VALUES ('".$tag_id."',
					  '".$tag."',
					  '".$title_url."',
					  '".$timestamp."')";
			$result = $db->Execute($query);

			#If there was a problem inserting the tag, stop
			#here and don't let the tag be associated
			if (!$result) {
				return false;
			}
		}

		#Associate the tag ... perform an update if this tag already
		#is associated.  An example of this is if a tag was added
		#to the system without a pair of coordinates (via an EXIF
		#tag) and the Lifebox tag was re-tagged with the mouse
		if ($tag_associated) {
			$query = "UPDATE ".$CONFIG['tbl_tag_photo_map']."
				  SET x1 = '".$x1."',
				      y1 = '".$y1."',
				      x2 = '".$x2."',
				      y2 = '".$y2."'
				  WHERE tag_id = '".$tag_id."' AND
					photo_id = '".$this->photo_id."'";

		#If this is a new association, just insert it
		} else {
			$query = "INSERT INTO ".$CONFIG['tbl_tag_photo_map']."
				  (tag_id,photo_id,x1,y1,x2,y2,email)
				  VALUES ('".$tag_id."',
					  '".$this->photo_id."',
					  '".$x1."',
					  '".$y1."',
					  '".$x2."',
					  '".$y2."',
					  '".$email."')";
		}
		$result = $db->Execute($query);

		#Update the tag index
                $this->updateTagIndex();

		if ($result) {

			#Update the tag count in the tags table
			$query = "UPDATE ".$CONFIG['tbl_tags']."
				  SET count = count + 1
				  WHERE tag_id = '".$tag_id."'";
			$result = $db->Execute($query);

			if (!$result) {
				return false;
			}


			#Update the tag count for the photo
                        $query = "UPDATE ".$CONFIG['tbl_photos']."
                                  SET tags = tags + 1
                                  WHERE photo_id = '".$this->photo_id."'";
                        $result = $db->Execute($query);

                        if (!$result) {
                                return false;
                        }

			#Check if the tag is a name, and if it is, e-mail the person
			$name_array = Text::getNameArray($tag);
			$first = '';
			$last = '';
			if (isset($name_array['first_name'])) $first = $name_array['first_name'];
			if (isset($name_array['last_name'])) $last = $name_array['last_name'];

			$query = "SELECT email
				  FROM ".$CONFIG['tbl_access']."
				  WHERE first = '$first' AND
					last = '$last'";
			$row = $db->getRow($query);

			if (isset($row['email'])) {
				$email = $row['email'];
			}

			#Figure out if this is a photo or video, and adjust the text
                        #accordingly
                        if ($this->info['is_video'] == 1) {
                                $media_type = 'video';
                        } else {
                                $media_type = 'photo';
                        }

			#If X and Y coordinates have been submitted, send out an e-mail tag notification,
                        #otherwise, just let the person know that they have somehow been associated with
                        #a picture or video
                        if ($x1 OR $x2 OR $y1 OR $y2) {
				$tag_type = 'tagged';
                        } else {
				$tag_type = 'associated';
                        }

			#Figure out whether this is for an album or roll, and adjust the text accordingly
                        if ($this->info['type'] === '1') {
                                $album_type = 'roll';
                        } else {
                                $album_type = 'album';
                        }

			if ($email AND !$tag_associated AND $this->info['album_title_url'] AND 
			    $this->info['photo_title_url'] AND $this->info['photo_id']) {
				$this->addToTagQueue($first,$last,$email,$this->info['photo_id'],
						     $this->info['album_title_url'],$this->info['photo_title_url'],
						     $tag_type,$media_type,$album_type);

			}
	
			return true;
			
		} else {
			return false;
		}
	}

	/*****************************************************************
	 * Delete a tag from the system
         *****************************************************************/
        function deleteTag($tag) {
	global $CONFIG;
	global $db;
	global $_USER;

		if ($_USER['level'] < $CONFIG['permission_delete_photo_tags']) {
                        return false;
                }

		$tag = trim($tag);
		$tag = addslashes($tag);

		#First, count the number of times this tag is in use and grab
		#the tag_id which we can use in the next query;
		$query = "SELECT COUNT(*) as num,
				 t.tag_id as tag_id
			  FROM ".$CONFIG['tbl_tag_photo_map']." tpm,
			       ".$CONFIG['tbl_tags']." t
			  WHERE tpm.tag_id = t.tag_id AND
				t.title = '$tag'
			  GROUP BY t.tag_id";
		$row = $db->getRow($query);
		$num_tags = $row['num'];
		$tag_id = $row['tag_id'];

		if (!$row) {
			return false;
		}

		#Delete the tag - photo map
		$query = "DELETE FROM ".$CONFIG['tbl_tag_photo_map']."
			  WHERE photo_id = '".$this->photo_id."' AND
				tag_id = '$tag_id'";
		$result = $db->Execute($query);

		if (!$result) {
			return false;
		} else {
			#We now have one less tag
			$num_tags--;
		}

		#If this is the last tag in the sytem, delete it from the tags table
		if ($num_tags == 0) {
			$query = "DELETE FROM ".$CONFIG['tbl_tags']."
				  WHERE tag_id = '$tag_id'";
			$result = $db->Execute($query);

			if (!$result) {
				return false;
			}
		} else {
		
			#Update the tag count in the tabs table
                        $query = "UPDATE ".$CONFIG['tbl_tags']."
                                  SET count = $num_tags
                                  WHERE tag_id = '".$tag_id."'";
                        $result = $db->Execute($query);

                        if (!$result) {
                                return false;
                        }

		}

                #Update the tag count for the photo
                $query = "UPDATE ".$CONFIG['tbl_photos']."
                          SET tags = tags - 1
                          WHERE photo_id = '".$this->photo_id."'";
                $result = $db->Execute($query);

                if (!$result) {
                	return false;
                }

		#Update the tag index
                $this->updateTagIndex();

		return true;

	}

	/*****************************************************************
	 * When a person is tagged, instead of sending out an email
	 * directly, add the new information to a queue to group together
	 * multiple tags for the same person ... this prevents spam !
         *****************************************************************/
	function addToTagQueue($to_first,$to_last,$to_email,$photo_id,
			       $album_title_url,$photo_title_url,
			       $tag_type,$media_type,$album_type) {
	global $CONFIG;
	global $db;
	global $ERROR;
	global $_USER;

		$timestamp = time();
		$timestamp_created = $timestamp;
		$timestamp_sent = 0;
		$sent = 0;

		#Generate a unique tag_queue_id
		$tag_queue_id = md5($to_first.$to_last.$timestamp);

		$query = "INSERT INTO ".$CONFIG['tbl_tag_queue']."
			  (tag_queue_id,from_first,from_last,to_first,
			   to_last,to_email,photo_id,album_title_url,
			   photo_title_url,tag_type,media_type,album_type,
			   timestamp_created,timestamp_sent,sent)
			  VALUES('$tag_queue_id',
				 '".$_USER['first']."',
				 '".$_USER['last']."',
				 '".$to_first."',
				 '".$to_last."',
				 '".$to_email."',
				 '".$photo_id."',
                                 '".$album_title_url."',
                                 '".$photo_title_url."',
				 '".$tag_type."',
                                 '".$media_type."',
                                 '".$album_type."',
				 '".$timestamp_created."',
				 '".$timestamp_sent."',
				 '".$sent."')";
		$result = $db->Execute($query);

		if (!$result) {
			return false;
		}
	}	 

	/*****************************************************************
	 * Groups together tags from albums and rolls for each person
	 * tagged and sends them out in a chunk to minimize emails
         *****************************************************************/
	function flushTagQueue() {
	global $CONFIG;
	global $db;
	global $ERROR;

		$query = "SELECT *
			  FROM ".$CONFIG['tbl_tag_queue']."
			  WHERE sent = '0'";
		$results = $db->getAll($query);

		if (empty($results)) {
			return true;
		}

		#Build the group for email processing
		foreach ($results as $row) {

			#Increment photo and video counters
			if ($row['media_type'] === 'photo') {
				$group[$row['to_email']][$row['from_first'].' '.$row['from_last']]['num_photos']++;
			}

			if ($row['media_type'] === 'video') {
                                $group[$row['to_email']][$row['from_first'].' '.$row['from_last']]['num_videos']++;
                        }

			#Fetch the information about the given photo_id
			$photo = new Photo($row['album_title_url'],$row['photo_title_url']);
			$photo_info = $photo->getAll();

			#Only include the item if the photo can be found ... this protects against
			#deletions or if a photo is renamed twice.
			if ($photo_info) {
				$album_title = html_entity_decode(ucwords($row['album_type']).' '.$photo_info['album_title']);
		
				$group[$row['to_email']][$row['from_first'].' '.$row['from_last']][$album_title][$photo_info['photo_title']]['album_title'] = 
											$album_title;

				$group[$row['to_email']][$row['from_first'].' '.$row['from_last']][$album_title][$photo_info['photo_title']]['photo_title'] = 
											$photo_info['photo_title'];

				$group[$row['to_email']][$row['from_first'].' '.$row['from_last']][$album_title][$photo_info['photo_title']]['media_type'] =
											$row['media_type'];

				$group[$row['to_email']][$row['from_first'].' '.$row['from_last']][$album_title][$photo_info['photo_title']]['tag_type'] =
											$row['tag_type'];

				$group[$row['to_email']][$row['from_first'].' '.$row['from_last']][$album_title][$photo_info['photo_title']]['url'] = 
											$CONFIG['full_site'].
											$photo_info['album_title_url'].'/'.
											$photo_info['photo_title_url'];

				#Remove the emailed person from the other people tags list
				$tag_list = $photo_info['tag_list'];
				$tag_list = trim($tag_list);
				$tag_list = str_replace($row['to_first'].' '.$row['to_last'].',','',$tag_list);
				$tag_list = str_replace($row['to_first'].' '.$row['to_last'],'',$tag_list);
				$tag_list = trim($tag_list);

				#If there's a stray comma, remove it
				if (substr($tag_list,-1) === ',') {
					$tag_list = substr($tag_list,0,-1);
				}
				
				#If there are only 2 people in the list, replace a command with an and
				if (substr_count($tag_list,',') == 1) {
					$tag_list = str_replace(', ',' and ',$tag_list);
				}

				#Replace the last comma with an ", and" for lists containing
				#multiple people
				if (substr_count($tag_list,',') > 1) {
					$last_comma_pos = strrpos($tag_list,',');
					$first_half = substr($tag_list,0,$last_comma_pos);
					$second_half = substr($tag_list,$last_comma_pos);
					$second_half = str_replace(', ',', and ',$second_half);
					$tag_list = $first_half.$second_half;
				}

				$group[$row['to_email']][$row['from_first'].' '.$row['from_last']][$album_title][$photo_info['photo_title']]['tag_list'] = 
											$tag_list;
			}
		}

		if (count($group) == 0) {
			return false;
		}

		foreach ($group as $to_email=>$senders) {
			foreach ($senders as $from_name=>$album) {
				$num_photos = $album['num_photos'];
	                        $num_videos = $album['num_videos'];

				$space_pos = strpos($from_name,' ');
				$first = substr($from_name,0,$space_pos);
				$message = "$first tagged you in the following media:\r\n\r\n";


				foreach ($album as $album_title=>$photos) {

					if ($album_title !== 'num_photos' AND $album_title !== 'num_videos') {

						#Generate photo text
						if ($num_photos == 1) {
							$photo_text = "$num_photos photo";
						} elseif ($num_photos > 1) {
							$photo_text = "$num_photos photos";
						} else {
							$photo_text = false;
						}

						#Generate video text
						if ($num_videos > 0) {
							if ($num_videos == 1) {
								$video_text = 'video';
							} else {
								$video_text = 'videos';
							}

							if ($photo_text) {
								$and_text = ' and ';
							} else {
								$and_text = '';
							}

							$video_extension = $and_text.$num_videos.' '.$video_text;
						} else {
							$video_extension = '';
						}

						$subject = "$from_name tagged you in ".$photo_text.$video_extension;
						$from = $CONFIG['email_from_tag'];
			
						$message .= $album_title."\r\n";

						#Underline the album title exactly to the length
						#of the album title
						for ($i=0; $i<strlen($album_title); $i++) {
							$message .= '-';
						}

						#Add a few extra to adjust for non-fixed fonts (most clients)
						$message .= '---------------';
						$message .= "\r\n";

						foreach ($photos as $photo_title=>$value) {
							if ($value['tag_list']) {
								$with_string = ' with '.$value['tag_list'];
							} else {
								$with_string = '';
							}

							$media_type = $value['media_type'];
							$tag_type = ucwords($value['tag_type']);

							$message .= $tag_type.' in '.$media_type.' "'.$photo_title.'"'.$with_string."\r\n";
							$message .= $value['url']."\r\n\r\n";
						}

						$message .= "\r\n";
					}
				}
				
				#Strip the last white space from the end of the e-mail
				$message = substr($message,0,-6);

				#Update the tag queue and set tags as sent	
				#We do this first to minimize the possibility of 
				#duplicate e-mails being sent.
                                $timestamp = time();
                                $query = "UPDATE ".$CONFIG['tbl_tag_queue']."
                                          SET sent = '1',
                                              timestamp_sent = '$timestamp'
                                          WHERE to_email = '$to_email'";
                                $result = $db->Execute($query);

				$m = new Mail();
				$m->setTo($to_email);
				$m->setFrom($CONFIG['email_from_tag']);
				$m->setSubject($subject);
				$m->setTextMessage($message);

				$m->send();

				}
			}

		return true;
	}

	/*****************************************************************
	 * Automatically flush the tag queue after new tags have not been
	 * added after a certain amount of time
         *****************************************************************/
	function autoFlushTagQueue() {
	global $CONFIG;
	global $db;

		$query = "SELECT MAX(timestamp_created) as most_recent
			  FROM ".$CONFIG['tbl_tag_queue']."";
		$row = $db->getRow($query);

		$time = time();

		if ($time - $row['most_recent'] > $CONFIG['tag_queue_flush_time']) {
			$this->flushTagQueue();
		}
	}

	/*****************************************************************
	 * Return all information about an image
         *****************************************************************/
	function getAll() {

		return $this->info;

	}

	/*****************************************************************
	 * Count Photos
         *****************************************************************/
        function countPhotos($level=false) {
	global $CONFIG;
	global $db;
	global $_USER;

		if ($level){
			$where = "WHERE level <= ".$_USER['level'];
		}

		$query = "SELECT COUNT(*) as num
			  FROM ".$CONFIG['tbl_photos']."
			  $where";
		$row = $db->CacheGetRow($CONFIG['cache_statistics'],$query);

		if ($row){
			return $row['num'];
		} else {
			return false;
		}
	}        

	/*****************************************************************
	 * Update views
         *****************************************************************/
	function updateViews() {
	global $CONFIG;
	global $db;
	global $_USER;

		#Posts don't count as views ...	
		if ($_POST) {
			return false;
		}

		if (isset($_SERVER['HTTP_REFERER'])) { $referer = addslashes($_SERVER['HTTP_REFERER']); } else { $referer = ''; }
		$query = "INSERT INTO ".$CONFIG['tbl_views']."
                          (ip, host, photo_id, referer, url,  timestamp_created, first, last, email)
                          VALUES ('".$_SERVER['REMOTE_ADDR']."',
                                  '".gethostbyaddr($_SERVER['REMOTE_ADDR'])."',
                                  '".$this->photo_id."',
                                  '".$referer."',
                                  'http://".addslashes($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'])."',
                                   now(),
                                  '".$_USER['first']."',
                                  '".$_USER['last']."',
                                  '".$_USER['email']."')";
                $result = $db->Execute($query);

                $query = "SELECT COUNT(*) as num
                          FROM views
                          WHERE ip = '".$_SERVER['REMOTE_ADDR']."' AND
                                url = 'http://".addslashes($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'])."'";
                $result = $db->Execute($query);

                $row = $db->getRow($query);
		$timestamp = time();

                if ($row['num'] <= 1){
                        $query = "UPDATE photos
                                  SET views = views + 1,
				      timestamp_last_view = '$timestamp',
				      last_viewer = '".$_USER['id']."'
                                  WHERE photo_id = '".$this->photo_id."'";
                        $result = $db->Execute($query);
                }

	}

	/*****************************************************************
	 * Update Permissions
         *****************************************************************/
	function changeLevel($level_new) {
	global $CONFIG;
	global $db;
	global $_USER;

		if ($_USER['level'] < $CONFIG['permission_change_level']) {
			return false;
		}

		#Check to make sure this is not a highlight image
		if (Album::isHighlight($this->photo_id) AND $level_new !== '1') {
			return false;
		}

		$timestamp = time();

		$log = new Log($CONFIG['changelog_photo_permission']);
		
		if ($log->addEvent($this->photo_id,$this->info['level'],$level_new)) {
			$query = "UPDATE ".$CONFIG['tbl_photos']."
				  SET level = '$level_new'
				  WHERE photo_id = '".$this->photo_id."'";
			$result = $db->Execute($query);

			if (!$result) {
				return false;
			}

			#Update Level Cache ################################
			# 1- Select all albums for which the photo appears
			# 2- For each album, find the minimum level
			# 3- Update that minimum level in the albums table

			$query = "SELECT album_id
				  FROM ".$CONFIG['tbl_album_photo_map']."
				  WHERE photo_id = '".$this->photo_id."'";
			$results = $db->getAll($query);

			foreach ($results as $r) {
				$album_id = $r['album_id'];

		                $query = "SELECT a.album_id as album_id,
						 MIN(level) as min_level
		                          FROM ".$CONFIG['tbl_photos']." p, 
		                               ".$CONFIG['tbl_album_photo_map']." apm, 
		                               ".$CONFIG['tbl_albums']." a 
		                          WHERE a.album_id = apm.album_id 
		                               AND apm.photo_id = p.photo_id
					       AND a.album_id = '$album_id' 
		     	                  GROUP BY a.album_id;";
	       	         	$row = $db->getRow($query);
				
		                $query = "UPDATE ".$CONFIG['tbl_albums']."
		                          SET min_level = '".$row['min_level']."'
		                          WHERE album_id = '".$row['album_id']."'";
		                $result = $db->Execute($query);
			}

	                ####################################################

		return true;

		}
	}

	/*****************************************************************
	 * Get Albums
         *****************************************************************/
	function getAlbums() {
	global $CONFIG;
	global $db;
	global $_USER;

		$query = "SELECT a.title,
				 a.title_url,
				 a.type
			  FROM ".$CONFIG['tbl_photos']." p,
                                       ".$CONFIG['tbl_albums']." a,
                                       ".$CONFIG['tbl_album_photo_map']." apm
                                  WHERE p.photo_id = apm.photo_id AND
                                        apm.album_id = a.album_id AND
					p.level <= ".$_USER['level']." AND
					a.level <= ".$_USER['level']." AND
				        p.photo_id = '$this->photo_id'";
		$results = $db->CacheGetAll(0,$query);
		
		if ($results) {
			return $results;
		} else {
			return false;
		}
	}

	/*****************************************************************
         * Get All Albums Array ignoring privacy restrictions
         *****************************************************************/
        function getAllAlbumsArray() {
	global $CONFIG;
	global $db;

		$query = "SELECT a.album_id
			  FROM ".$CONFIG['tbl_albums']." a,
			       ".$CONFIG['tbl_album_photo_map']." apm
			  WHERE a.album_id = apm.album_id AND
				apm.photo_id = '$this->photo_id'";
		$results = $db->getAll($query);

		if ($results) {
			for ($i=0; $i<count($results); $i++) {
				$array[$results[$i]['album_id']] = $results[$i]['album_id'];
			}
			return $array;
		} else {
			return false;
		}
	}

	/*****************************************************************
	 * Increment the comment counter by 1
         *****************************************************************/
        function increaseComments() {
	global $CONFIG;
	global $db;

		$timestamp = time();
		$query = "UPDATE ".$CONFIG['tbl_photos']."
			  SET comments = comments + 1,
			      timestamp_last_comment = $timestamp
			  WHERE photo_id = '".$this->photo_id."'";
		$result = $db->Execute($query);

		if ($result) {
			return true;
		} else {
			return false;
		}
	}

	/*****************************************************************
         * Increment the comment counter by 1
         *****************************************************************/
        function decreaseComments() {
        global $CONFIG;
        global $db;

                $query = "UPDATE ".$CONFIG['tbl_photos']."
                          SET comments = comments - 1
                          WHERE photo_id = '".$this->photo_id."'";
                $result = $db->Execute($query);

                if ($result) {
                        return true;
                } else {
                        return false;
                }
        }

	/*****************************************************************
	 * Calculates a unique visual photo id from a supplied file
         *****************************************************************/
	function getPhotoID($file) {
	global $CONFIG;

		$filename_array = Photo::splitMediaFilename($file);
		$filename_extension = $filename_array['filename_extension'];
		$filename_noextension = $filename_array['filename_noextension'];
		$filename = $filename_array['filename'];    

		if (in_array(strtolower($filename_extension),$CONFIG['video_files'])) {
			$photo_id = trim(shell_exec('ffmpeg -i "'.$file.'" -vframes 1 -f mjpeg - | '.$CONFIG['identify_path'].' -format "%#" -'));
		} else {
			$photo_id = trim(shell_exec(''.$CONFIG['identify_path'].' -format "%#" "'.$file.'[0]"'));
		}

		#Double check that the generated ID is exactly 64 characters
		if (strlen($photo_id) != 64) {
			return true;
		} else {
			return $photo_id;
		}
	}

	/*****************************************************************
	 * Given a filename, find the filename extension and noextension 
	 * parts
         *****************************************************************/
	function splitMediaFilename($filename) {
	global $CONFIG;

		#If a full site was specified, remove it
		$filename = str_replace($CONFIG['full_site'],'',$filename);

		#If the given filename is a full path, extract just the name
		if (strpos($filename,'/') OR substr($filename,0,1) == '/') {
			$last_slash = strrpos($filename,'/');
			$filename = substr($filename,$last_slash+1);
		}

		#Get Extension and No Extension parts of the filename
		if (strpos($filename,'.')) {
			$filename_period = strpos($filename,'.');
			$filename_noextension = substr($filename,0,$filename_period);
			$filename_extension = substr($filename,$filename_period+1);
			$filename_extension_lower = strtolower($filename_extension);
			$filename = $filename_noextension.'.'.$filename_extension;

		 #Handle files without an extension
		 } else {
			$filename_noextension = $filename;
		 }

		$array['filename_extension'] = $filename_extension;
		$array['filename_noextension'] = $filename_noextension;
		$array['filename'] = $filename;

		return $array;

	}

	/*****************************************************************
	 * Updated discoverMedia function
	 *****************************************************************/
	function discoverMedia($root_directory=false,$quickscan=true,$print=false) {
	global $CONFIG;
	global $db;

		#Turn off the maximum execution time ... this can take awhile
                set_time_limit(0);  

		if (!$root_directory) {
			$root_directory = $CONFIG['media_path'];
		}

		#Fix double slashes just in case
		$root_directory = str_replace('//','/',$root_directory);

		// set filenames invisible if you want 
		$invisibleFileNames = array(".", "..", ".htaccess", ".htpasswd");

		// run through content of root directory
		$dirContent = scandir($root_directory);
    		$allData = array();
    
		// file counter gets incremented for a better 
    		$fileCounter = 0;
    		foreach($dirContent as $key => $content) {
       
			#If printing is enabled, turn on the output buffer
			if ($print) if (ob_get_level() == 0) ob_start();

			// filter all files not accessible
			$full_path = $root_directory.'/'.$content;

			# Remove double // just in case
			$full_path = str_replace('//','/',$full_path);

			if(!in_array($content, $invisibleFileNames) AND
			   substr($content,0,1) !== '.') {
		    
				// if content is file & readable, add to array
				if(is_file($full_path) && is_readable($full_path)) {

					####################################################################
					# For each file we recurse, there are serveral possible
					# states of the file.  We categorize each photo or video
					# into one of the below states
					# 1. NEW PATH
					#	A. New MD5 Sum
					#	   (New file added to Lifebox)
					#		a. New Visual SHA Sum (INSERT)
					#		   (New file, or original file visually modified
					#		    which we consider a new file).
					#		b. Old Visual SHA Sum (UPDATE)
					#		   (A file was renamed and modified at the same
					#		    time, but visually is the exact same.  Maybe
					#		    a file was exported to a new name and the EXIF
					#		    time was updated.  This is a rare but possible
					#		    case.
					#	B. Old MD5 Sum (UPDATE)
					#	   (Existing file moved to a new directory)
					# 2. EXISTING PATH
					#	A. New MD5 Sum
					#	   (Original file updated, maybe new EXIF info)
					#               a. New Visual SHA Sum (INSERT)
                                        #                  (New file, or original file visually modified
                                        #                   which we consider a new file).
                                        #               b. Old Visual SHA Sum (UPDATE)
                                        #                  (A file was renamed and modified at the same
                                        #                   time, but visually is the exact same.  Maybe
                                        #                   a file was exported to a new name and the EXIF
                                        #                   time was updated.  This is a rare but possible
                                        #                   case.
					#	B. Old MD5 Sum
					#	   (No changes, not a new file)	
					####################################################################

					# First, find the relative path ... this is the path
					# of the file from a directory above the ORIGINALS/MODIFIED
					# directory, up to the filename.
                                        $path_relative = $this->getRelativePath($full_path);

					# Find the filename, filename_extension, filename_noextension
					# From the given full_path
					$filename_array = $this->splitMediaFilename($full_path);	
					$filename_extension = $filename_array['filename_extension'];
					$filename_noextension = $filename_array['filename_noextension'];
					$filename = $filename_array['filename'];

					#Determine if this is an acceptable file to be scanning
					if (in_array(strtolower($filename_extension),$CONFIG['acceptable_media'])) {
						$acceptable_media = true;	
                			} else {
						$acceptable_media = false;
					}

					# Next, we decide whether to branch off to (1) or (2).
					# We check to see if the path and filename of the scanned 
					# file exists somewhere in the database.  We also grab the
					# md5 hash which we'll use in step (2) if necessary.
                                        $query = "SELECT path
                                                  FROM photos
                                                  WHERE path = '".addslashes($path_relative)."' AND
                                                        filename = '".addslashes($filename)."'";
                                        $row = $db->getRow($query);

					####################################################################
					# (1) Path does not exist
					# ASSUME NEW FILE
					####################################################################
					if (empty($row['path']) AND $acceptable_media) {
					
						#We now calculate the md5 sum of the scanned file to see
						#if we should fork to (1A) New MD5 Sum or (1B) Old MD5 Sum
						$photo_md5 = md5_file($full_path);

						#Check if an XML sidecar exists
						$full_path_sidecar = $this->_getSideCarPath($full_path);
						if (file_exists($full_path_sidecar)) {
							$sidecar_md5 = md5_file($full_path_sidecar);
							$sidecar_addition = "AND sidecar_md5 = '$sidecar_md5'";
						} else {
                                                        $sidecar_addition = false;
                                                }

						# Do a check to see if the md5 sum is already
						# in the database.
						$query = "SELECT photo_md5, sidecar_md5
							  FROM ".$CONFIG['tbl_photos']."
							  WHERE photo_md5 = '$photo_md5'
								$sidecar_addition";
						$row = $db->getRow($query);
						$db_photo_md5 = $row['photo_md5'];
						$db_sidecar_md5 = $row['sidecar_md5'];

						####################################################################
	      	                                # (1A) Path does not exist, New MD5 Sum
	                                        # This means a new file was added to lifebox
	                                        ####################################################################
						if (empty($db_photo_md5)) {
					
							# Now it's time to calculate the visual SHA sum
							# of the scanned image.  This process can take
							# a few seconds.	
							$photo_id = Photo::getPhotoID($full_path);

							# Do a check to see if the visual SHA sum is already
							# in the database.
							$query = "SELECT COUNT(*) as num
								  FROM ".$CONFIG['tbl_photos']."
								  WHERE photo_id = '$photo_id'";
							$row = $db->getRow($query);

							####################################################################
                                                	# (1Aa) New path, New MD5 Sum, New Visual SHA Sum, this is a brand 
							# new file !!  Either that or the original photo has visually changed
							# which we consider a new file.  Let's insert and index this.
                                               	 	####################################################################
							if ($row['num'] == 0) {

								$photo = new Photo();
								$this->photo_id = $photo_id;
								$this->photo_md5 = $photo_md5;
								$data = $photo->_scanPhoto($full_path,$photo_md5,$photo_id);
								$photo->setInfo($data);
								$photo->commit();

								#Display output if print is enabled
								if ($print) print "Added $full_path <br />";

								#Flush output buffer if print is enabled.                                       
			                                        if ($print) Photo::flushBuffer();

							####################################################################
                                                        # (1Ab) New path, New MD5 Sum, Existing Visual SHA Sum, this is a
							# file that's already in the database.  Maybe a file was exported to	
							# a new name AND the EXIF tags were updated.  This is a rare case
							# but nevertheless must be accounted for.
                                                        ####################################################################
							} else {

								//File moved to a new directory code goes here.
								//Also must rescan file for EXIF changes.
								$photo = new Photo($photo_id);
                                                                $data = $photo->_scanPhoto($full_path,$photo_md5,$photo_id);
                                                                $photo->setInfo($data);
                                                                $photo->commit();

								#Display output if print is enabled
                                                                if ($print) print "Updated $full_path <br />";
								
								#Flush output buffer if print is enabled.                                       
			                                        if ($print) Photo::flushBuffer();
	
							}
						####################################################################
                                                # (1B) Path does not exist, Old MD5 Sum
                                                # A file was moved to a new directory
                                                ####################################################################
						} else {
								
							//File moved to a new directory code goes here.
							$photo = new Photo($photo_md5);
							$data = $photo->_scanPhoto($full_path,$photo_md5);
							$photo->setInfo($data);
							$photo->commit();

							#Display output if print is enabled
                                                        if ($print) print "Updated $full_path <br />";
							
							#Flush output buffer if print is enabled.                                       
		                                        if ($print) Photo::flushBuffer();

						}

					####################################################################
                                        # (2) Path exists
                                        # This is not a new file, an update of some kind may have occurred.
					# Now, we have to figure out what, if anything, has changed.
                                        ####################################################################
					} else if ($acceptable_media) {

						#We now calculate the md5 sum of the scanned file to see
                                                #if we should fork to (1A) New MD5 Sum or (1B) Old MD5 Sum
                                                $photo_md5 = md5_file($full_path);

						#Check if an XML sidecar exists
                                                $full_path_sidecar = $this->_getSideCarPath($full_path);
                                                if (file_exists($full_path_sidecar)) {
                                                        $sidecar_md5 = md5_file($full_path_sidecar);
                                                        $sidecar_addition = "AND sidecar_md5 = '$sidecar_md5'";
                                                } else {
							$sidecar_addition = false;
						}

						# Do a check to see if the md5 sum is already
                                                # in the database.
                                                $query = "SELECT photo_md5, sidecar_md5
                                                          FROM ".$CONFIG['tbl_photos']."
                                                          WHERE photo_md5 = '$photo_md5'
								$sidecar_addition";

                                                $row = $db->getRow($query);
                                                $db_photo_md5 = $row['photo_md5'];
                                                $db_sidecar_md5 = $row['sidecar_md5'];
	
						####################################################################
                                                # (2A) Path exists but New MD5 Sum
						# This means an existing file was updated, perhaps the exif time
						# was modified
                                                ####################################################################
						if (empty($db_photo_md5)) {

							# Now it's time to calculate the visual SHA sum
                                                        # of the scanned image.  This process can take
                                                        # a few seconds.        
                                                        $photo_id = Photo::getPhotoID($full_path);

                                                        # Do a check to see if the visual SHA sum is already
                                                        # in the database.
                                                        $query = "SELECT COUNT(*) as num
                                                                  FROM ".$CONFIG['tbl_photos']."
                                                                  WHERE photo_id = '$photo_id'";
                                                        $row = $db->getRow($query);

                                                        ####################################################################
                                                        # (2Aa) Old path, New MD5 Sum, New Visual SHA Sum, this is a brand 
                                                        # new file !!  Either that or the original photo has visually changed
                                                        # which we consider a new file.  Let's insert and index this.
                                                        ####################################################################
                                                        if ($row['num'] == 0) {
                                        
								$photo = new Photo();
								$this->photo_id = $photo_id;
                                                                $this->photo_md5 = $photo_md5;
                                                                $data = $photo->_scanPhoto($full_path,$photo_md5,$photo_id);
                                                                $photo->setInfo($data);
                                                                $photo->commit();
                       
								#Display output if print is enabled
                                                                if ($print) print "Added $full_path <br />";
 
								#Flush output buffer if print is enabled.                                       
			                                        if ($print) Photo::flushBuffer();

                                                        ####################################################################
                                                        # (2Ab) Old path, New MD5 Sum, Existing Visual SHA Sum, this is a
                                                        # file that's already in the database.  Maybe a file was exported to    
                                                        # a new name AND the EXIF tags were updated.  This is a rare case
                                                        # but nevertheless must be accounted for.
                                                        ####################################################################
                                                        } else {

								$photo = new Photo($photo_id);
                                                                $data = $photo->_scanPhoto($full_path,$photo_md5,$photo_id);
                                                                $photo->setInfo($data);
                                                                $photo->commit();

								#Display output if print is enabled
                                                                if ($print) print "Updated $full_path <br />";
                                                       
								#Flush output buffer if print is enabled.                                       
			                                        if ($print) Photo::flushBuffer();
 
							}

						####################################################################
                                                # (2B) Path exists and old MD5 Sum
						# Path is the same, md5sum is the same, this file is already 
						# in the database, everything's good, no need to do anything !!
                                                ####################################################################
						} else {
							// Do nothing
							#Display output if print is enabled
                                                        if ($print) print "Skipped $full_path <br />";

						}
						
					}
					
					$fileCounter++;

					#Flush output buffer if print is enabled.					
					if ($print) Photo::flushBuffer();

				// if content is a directory and readable, add path and name
				} elseif(is_dir($full_path) && is_readable($full_path)) {

					if ($quickscan) {
						#Retrieve the directory from the dir_scan_cache
						#No reason to re-scan if there haven't been any
						#changes
						$query = "SELECT *
							  FROM ".$CONFIG['tbl_dir_scan_cache']."
							  WHERE full_path = '".addslashes($full_path)."'";
						$row = $db->getRow($query);

						if (!$row AND $full_path) {
							$query = "INSERT INTO ".$CONFIG['tbl_dir_scan_cache']."
								  (full_path,folder_original_ctime,folder_original_mtime)
								  VALUES('".addslashes($full_path)."',
									 '".filectime($full_path)."',
									 '".filemtime($full_path)."')";
							$result = $db->Execute($query);
						}
					}
	
					#If quick scan has been disabled OR if
					#This is an iPhoto library with subfolders of DDDD	
					if (!$quickscan OR is_numeric(substr($full_path,-4))) {

						// recursive callback to open new directory
                                                $this->discoverMedia($full_path,$quickscan);
	
					} else if (filectime($full_path) != $row['folder_original_ctime'] OR
					           filemtime($full_path) != $row['folder_original_mtime']) {	      
					
						// recursive callback to open new directory
						$this->discoverMedia($full_path);

						$query = "UPDATE ".$CONFIG['tbl_dir_scan_cache']."
							  SET folder_original_ctime = '".filectime($full_path)."',
							      folder_original_mtime = '".filemtime($full_path)."'
							  WHERE full_path = '".addslashes($full_path)."'";
						$result = $db->Execute($query);

					}
				}
			}
    		}
		
	}

	/*****************************************************************
	 * Read information from a video file
         *****************************************************************/
	function getVideoTags($file) {
	global $CONFIG;
	
		$exec = $CONFIG['mediainfo_path'].' "'.$file.'"';	
		$output = shell_exec($exec);
		$array = explode("\n",$output);

		for($i=0; $i<count($array); $i++) {
			$array[$i] = str_replace('Config_Text_Separator','',$array[$i]);

			if (substr($array[$i],0,11) === 'Tagged_Date') {
				$return['datetime_tagged'] = strtotime(substr($array[$i],12));
				$return['datetime_tagged'] = date("Y-m-d H:i:s", $return['datetime_tagged']);
			}

			if (substr($array[$i],0,12) === 'Encoded_Date') {
                                $return['datetime_encoded'] = strtotime(substr($array[$i],12));
				$return['datetime_encoded'] = date("Y-m-d H:i:s", $return['datetime_encoded']);
                        }

			if (substr($array[$i],0,13) === 'Mastered_Date') {
				$return['datetime_mastered'] = strtotime(substr($array[$i],12));
                                $return['datetime_mastered'] = date("Y-m-d H:i:s", $return['datetime_mastered']);
			}

			if (empty($return['datetime_tagged']) AND !empty($return['datetime_mastered'])) {
				  $return['datetime_tagged'] = $return['datetime_mastered'];
			}

			if (empty($return['datetime_encoded']) AND !empty($return['datetime_mastered'])) {
                                  $return['datetime_encoded'] = $return['datetime_mastered'];
                        }

		}

		return $return;

	}

	/*****************************************************************
	 * Perform various cleaning operations
         *****************************************************************/
	function cleanPhotos($operation='all',$print=false) {
	global $CONFIG;
	global $db;

		#If printing is enabled, turn on the output buffer
                if ($print) if (ob_get_level() == 0) ob_start();

		if ($operation === '1' OR
		    $operation === '2' OR
		    $operation === '3' OR
		    $operation === '4' OR
		    $operation === 'all') {
			#Fetch all the photos from the database and perform
			#the below operations
			$query = "SELECT photo_id,path,filename,title,title_url
				  FROM ".$CONFIG['tbl_photos']."";
			$results = $db->getAll($query);
			for($i=0; $i<count($results); $i++) {

				$photo = new Photo($results[$i]['photo_id']);

				#Turn off time limit restriction
				set_time_limit(0);

				$original_full_path = $CONFIG['media_path'].$results[$i]['path'].$results[$i]['filename'];

				// 1 -----------------------------------------------------------------
				#Make sure that the physical file still exists.  If it doesn't, remove
				#This photo from the database.  All comments and tags will still
				#Be preserved.
				if ($operation === '1' OR 
				    $operation === 'all' ) {	
					if (!file_exists($original_full_path)) {
						$query = "DELETE FROM ".$CONFIG['tbl_photos']."
							  WHERE photo_id = '".$results[$i]['photo_id']."'";
						#Display output if print is enabled
                                                if ($print) print "Deleted $original_full_path <br />";
						$result = $db->Execute($query);
					}
				}
				// 2 -----------------------------------------------------------------
				#Make sure that each photo is a member of its Roll.
				#This should essentially never happen; but just in case.
				if ($operation === '2' OR
				    $operation === 'all'  ) {
					$query = "SELECT COUNT(*) as num
						  FROM albums a,
						       album_photo_map apm
						  WHERE a.album_id = apm.album_id AND
							apm.photo_id = '".$results[$i]['photo_id']."' AND
							a.type = '1' ";
					$row = $db->getRow($query);

					if ($row['num'] < 1) {

						#Remove the leading and trailing slashes from
						#the path extracted from the database
						$roll_title = $results[$i]['path'];
						if (substr($roll_title,-1) === '/') {
							$roll_title = substr($roll_title,0,-1);
						}

						$slash_pos = strrpos($roll_title,'/');
						$roll_title = substr($roll_title,$slash_pos+1);
					
						#Fetch the appropriate roll based on the found
						#path
						$query = "SELECT album_id
							  FROM albums
							  WHERE title = '".addslashes($roll_title)."' AND
							  type = '1'";
						$row = $db->getRow($query);	
						$album_id = $row['album_id'];
						$photo_id = $results[$i]['photo_id'];

						#Insert the photo --> roll mapping so that
						#this photo is no longer orphaned
						$query = "INSERT INTO ".$CONFIG['tbl_album_photo_map']."
							  (photo_id,album_id)
							  VALUES('$photo_id',
								 '$album_id')";
						$result = $db->Execute($query);

						#Display output if print is enabled
                                                if ($print) print "Inserted mapping $photo_id to $album_id <br />";

						$i++;
					}
				}

				// 3 -----------------------------------------------------------------
				# Check that every title_url is unique and if it's not, rename it
				# to a similar, but alternate name
				if ($operation === '3' OR
				    $operation === 'all' ) {
					$expected_title_url = Photo::getTitleURL($results[$i]['photo_id'],$results[$i]['title']);
					if ($results[$i]['title_url'] != $expected_title_url) {
						$query = "UPDATE photos
							  SET title_url_previous = '',
							      title_url = '".addslashes($expected_title_url)."'
							  WHERE photo_id = '".$results[$i]['photo_id']."'";
						$result = $db->Execute($query);

						#Display output if print is enabled
						if ($print) print $results[$i]['title_url']." Renamed to ".$expected_title_url." <br />";
					}
				}

				// 4 -----------------------------------------------------------------
				# Fetch all tags from the tag table and make sure a cached string
				# Is put into the photos table for searching.
				if ($operation === '4' OR
				    $operation === 'all' ) {
					$tag_string = $photo->getTagString();
					if ($tag_string) {
						$data = Array('tag_list' => $tag_string);
						$photo->setInfo($data);
						$photo->commit();

						#Display output if print is enabled
                                                if ($print) print "Updated $tag_string <br />";
					}
				}

				#Flush output buffer if print is enabled.
				if ($print) Photo::flushBuffer();
			}

			if ($print) print "Operation complete";
		}

		// 5 -----------------------------------------------------------------
		#Update the album counts
		if ($operation === '5' OR
		    $operation === '6' OR
		    $operation === 'all') {
			$query = "SELECT a.title,a.album_id, COUNT(p.photo_id) as num
				  FROM ".$CONFIG['tbl_albums']." a
				  LEFT JOIN album_photo_map apm ON a.album_id = apm.album_id
				  LEFT JOIN photos p ON p.photo_id = apm.photo_id
				  GROUP BY a.album_id";
			$results = $db->getAll($query);
			if ($results){
				foreach ($results as $row){
					$query = "UPDATE ".$CONFIG['tbl_albums']."
						  SET num_photos = '".$row['num']."'
						  WHERE album_id = '".$row['album_id']."'";
					$result = $db->Execute($query);
					#Display output if print is enabled
                                        if ($print) print "Updated album count for ".$row['title']." <br />";
					if ($print) Photo::flushBuffer();	
				}
				if ($print) print "All album counts updated.";
			}
		}

		// 6 -----------------------------------------------------------------
		#After the album counts have been updated
		#Delete albums AND mappings with unassociated photos
		if ($operation === '6' OR
		    $operation === 'all') {
			$query = "SELECT a.* 
				  FROM albums a 
				  WHERE num_photos = 0";
			$results = $db->Execute($query);
			if ($results) {
				foreach ($results as $row) {
					$query = "DELETE FROM albums
						  WHERE album_id = '".$row['album_id']."'";
					$db->Execute($query);

					$query = "DELETE FROM album_photo_map
						  WHERE album_id = '".$row['album_id']."'";
					$db->Execute($query);

					#Display output if print is enabled
                                        if ($print) print "Deleted album ".$row['title']." <br />";
					if ($print) Photo::flushBuffer();
				}
				if ($print) print "All superfluous album and album mappings deleted.";
			}
		}

		// 7 -----------------------------------------------------------------
		#Update Lowest Level (SECURITY) for each album
		if ($operation === '7' OR
		    $operation === 'all') {
			$query = "SELECT a.album_id, 
					 a.title,
					 MIN(p.level) as level
				  FROM photos p, 
				       album_photo_map apm, 
				       albums a 
				  WHERE a.album_id = apm.album_id 
				       AND apm.photo_id = p.photo_id 
				  GROUP BY album_id;";
			$results = $db->GetAll($query);

			foreach ($results as $row) {
				$query = "UPDATE albums
					  SET min_level = '".$row['level']."'
					  WHERE album_id = '".$row['album_id']."'";
				$db->Execute($query);
				#Display output if print is enabled
                                if ($print) print "Updated security for ".$row['title']." <br />";
				if ($print) Photo::flushBuffer();
			}
			if ($print) print "All security updated.";
		}

		// 8 -----------------------------------------------------------------
                #Update comments for each photo
		if ($operation === '8' OR
		    $operation === 'all') {

			#First, clear all counts
			$query = "UPDATE ".$CONFIG['tbl_photos']."
				  SET comments = ''";
			$db->Execute($query);

			#Now, regenerate the counts
			$query = "SELECT COUNT(*) as num,
					 photo_id
				  FROM ".$CONFIG['tbl_comments']."
				  GROUP BY photo_id";
			$results = $db->getAll($query);
			foreach ($results as $row) {
				$query = "UPDATE ".$CONFIG['tbl_photos']."
					  SET comments = '".$row['num']."'
					  WHERE photo_id = '".$row['photo_id']."'";
				$db->Execute($query);

				#Display output if print is enabled
                                if ($print) print "Updated comments (".$row['num'].") ".$row['photo_id']." <br />";
				if ($print) Photo::flushBuffer();
			}
			if ($print) print "Comment counts updated.";
		}

		// 9 -----------------------------------------------------------------
		# Re-initialize all modified times of the albums based on the most
		# recently modified time within each album
                if ($operation === '9' OR
                    $operation === 'all') {

			#Select all albums from the database
			$query = "SELECT album_id
				  FROM ".$CONFIG['tbl_albums']."";
			$results = $db->getAll($query);

			foreach ($results as $album) {
				$query = "SELECT MAX(EXIF_DateTimeOriginal) as EXIF_DateTimeOriginal,
						 MAX(EXIF_DateTimeDigitized) as EXIF_DateTimeDigitized
					  FROM ".$CONFIG['tbl_photos']." p,
					       ".$CONFIG['tbl_album_photo_map']." apm
					  WHERE p.photo_id = apm.photo_id AND
						apm.album_id = '".$album['album_id']."' AND
						p.is_video = '0'";
				$row = $db->getRow($query);

				if (isset($row['EXIF_DateTimeOriginal'])) {
					$datetime = $row['EXIF_DateTimeOriginal'];
				} elseif (isset($row['EXIF_DateTimeDigitized'])) {
					$datetime = $row['EXIF_DateTimeDigitized'];
				} else {
					$datetime = false;
				}

				if ($datetime) {
					$timestamp = strtotime($datetime);

					$query = "UPDATE ".$CONFIG['tbl_albums']."
						  SET timestamp_modified = '".$timestamp."'
						  WHERE album_id = '".$album['album_id']."'";
					$result = $db->Execute($query);

					#Display output if print is enabled
	                                if ($print) print "Updated modified time for ".$album['album_id']." <br />";
					if ($print) Photo::flushBuffer();

				}

				unset($datetime);
				unset ($row);
			}
			if ($print) print "All modified times updated.";
                }

		// 10 -----------------------------------------------------------------
		# Re-initialize all the creation times of albums based on the minimum
		# time EXIF tag found in each photo within that album
                if ($operation === '10' OR
                    $operation === 'all') {

                        #Select all albums from the database
                        $query = "SELECT album_id
                                  FROM ".$CONFIG['tbl_albums']."";
                        $results = $db->getAll($query);

                        foreach ($results as $album) {
                                $query = "SELECT MIN(EXIF_DateTimeOriginal) as EXIF_DateTimeOriginal,
                                                 MIN(EXIF_DateTimeDigitized) as EXIF_DateTimeDigitized
                                          FROM ".$CONFIG['tbl_photos']." p,
                                               ".$CONFIG['tbl_album_photo_map']." apm
                                          WHERE p.photo_id = apm.photo_id AND
                                                apm.album_id = '".$album['album_id']."' AND
						p.is_video = '0'";
                                $row = $db->getRow($query);

                                if ($row['EXIF_DateTimeOriginal'] > 0) {
                                        $datetime = $row['EXIF_DateTimeOriginal'];
                                } elseif ($row['EXIF_DateTimeDigitized'] > 0 ) {
                                        $datetime = $row['EXIF_DateTimeDigitized'];
                                } else {
                                        $datetime = false;
                                }

                                if ($datetime) {
                                        $timestamp = strtotime($datetime);

                                        $query = "UPDATE ".$CONFIG['tbl_albums']."
                                                  SET timestamp_created = '".$timestamp."'
                                                  WHERE album_id = '".$album['album_id']."'";
                                        $result = $db->Execute($query);

					#Display output if print is enabled
                                        if ($print) print "Updated creation time for ".$album['album_id']." <br />";
					if ($print) Photo::flushBuffer();

                                }

                                unset($datetime);
                                unset ($row);
                        }
			if ($print) print "All creation times updated.";
                }

		// 11 -----------------------------------------------------------------
		# Update the comment index for each photo which is used for search
		# indexing

		if ($operation === '11' OR
                    $operation === 'all') {
		
			$query = "SELECT photo_id
				  FROM ".$CONFIG['tbl_photos']."
				  WHERE comments > 0";
			$results = $db->getAll($query);

			foreach ($results as $photo) {
				Comment::updateCommentList($photo['photo_id']);

				#Display output if print is enabled
                                if ($print) print "Updated comment index for ".$photo['photo_id']." <br />";
				if ($print) Photo::flushBuffer();

			}
			if ($print) print "Comment index cache updated.";
		}

		// 12 -----------------------------------------------------------------
		# Send out all tag e-mails and empty the tag queue.
		if ($operation === '12' OR
                    $operation === 'all') {

			$photo = new Photo();	
			
			if ($photo->flushTagQueue()) {

				if ($print) print "All tag e-mails sent.";
			}
		}

		// 13 -----------------------------------------------------------------
                # Flush all existing thumbnail generated data
                if ($operation === '13' OR
                    $operation === 'all') {

			$query = "UPDATE photos
				  SET micro_generated = 0,
				      thumb_generated = 0,
				      preview_generated = 0,
				      full_generated = 0";
			$result = $db->Execute($query);

			if ($print) print "All thumbnail generation records flushed.  Generate thumbnails now.";
                }

		// 14 -----------------------------------------------------------------
		# Generate all new thumbnails
                if ($operation === '14' OR
                    $operation === 'all') {

			$query = "SELECT photo_id,path,filename
				  FROM ".$CONFIG['tbl_photos']."
				  WHERE thumb_generated = '' OR
					preview_generated = ''
				  ORDER BY path DESC, EXIF_DateTimeOriginal DESC";
			$results = $db->getAll($query);

			foreach ($results as $row) {
				$photo = new Photo($row['photo_id']);
				$photo->readPhoto('thumb',true);
				$photo->readPhoto('micro',true);
				$photo->readPhoto('preview',true);
				$photo->readPhoto('full',true);
				$photo->readPhoto('main_album',true);
				$photo->readPhoto('main_recent',true);
				if ($print) print "Thumbnails generated for ".$row['path'].$row['filename']."<br />";
				if ($print) Photo::flushBuffer();
			}
                }

		// 15 -----------------------------------------------------------------
                # Generate all thumbnails
                if ($operation === '15' OR
                    $operation === 'all') {

                        $query = "SELECT photo_id,path,filename
                                  FROM ".$CONFIG['tbl_photos']."
                                  ORDER BY path DESC, EXIF_DateTimeOriginal DESC";
                        $results = $db->getAll($query);

                        foreach ($results as $row) {
                                $photo = new Photo($row['photo_id']);
                                $photo->readPhoto('thumb',true);
				$photo->readPhoto('micro',true);
                                $photo->readPhoto('preview',true);
				$photo->readPhoto('full',true);
                                $photo->readPhoto('main_album',true);
                                $photo->readPhoto('main_recent',true);
                                if ($print) print "Thumbnails generated for ".$row['path'].$row['filename']."<br />";
                                if ($print) Photo::flushBuffer();
                        }
                }

		// 16 -----------------------------------------------------------------
                # Generate all videos
                if ($operation === '16' OR
                    $operation === 'all') {

                        $query = "SELECT photo_id,path,filename
                                  FROM ".$CONFIG['tbl_photos']."
				  WHERE is_video = 1
                                  ORDER BY path DESC, EXIF_DateTimeOriginal DESC";
                        $results = $db->getAll($query);

                        foreach ($results as $row) {
                                $photo = new Photo($row['photo_id']);
                                $photo->readPhoto('video');
                                if ($print) print "Video generated for ".$row['path'].$row['filename']."<br />";
                                if ($print) Photo::flushBuffer();
                        }
                }

	}

	/*****************************************************************
	 * Supplied with an original file path ... this will determine
	 * if a modified version exists in the modified path
         *****************************************************************/
	function modifiedExists($file) {
	global $CONFIG;

		$modified_file = $this->getModifiedFile($file);

		if (file_exists($modified_file)) {
			return true;
		} else {
			return false;
		}

	}

	/*****************************************************************
	 * Returns a modified path when a full path is specified
         *****************************************************************/
        function getModifiedFile($file) {
        global $CONFIG;

		$file = str_replace($CONFIG['media_path'],'',$file);
		$modified_path = $CONFIG['media_path_modified'].$file;

		$modified_path_period = strrpos($modified_path,'.');
		$modified_path_noextension = substr($modified_path,0,$modified_path_period);
		$modified_path_extension = substr($modified_path,$modified_path_period+1);

		if (strtolower($modified_path_extension) == $CONFIG['modified_extension']) {
			$modified_path = $modified_path_noextension.'.'.strtolower($modified_path_extension);
		} else {
			$modified_path = $modified_path_noextension.'.'.$CONFIG['modified_extension'];
		}

		return $modified_path;
        }

	/*****************************************************************
	 * Returns an original path
         *****************************************************************/
        function getOriginalFile($file) {
	global $CONFIG;

		$original_path = $CONFIG['media_path'].$file;

		$original_path = str_replace('//','/',$original_path);

		return $original_path;
	
	}

	/*****************************************************************
	 * Given a full file's path, this extracts everything up to the
	 * filename
         *****************************************************************/
        function getPathFromFile($file) {

                if (substr($file,-1) == '/') {
                        return $file;
                }

                $pos = strrpos($file,'/');
                $path = substr($file,0,$pos);

                if (substr($path,-1) == '/') {  
                        $path = substr($path,0,$pos-1);
                        return $path;
                } else {
                        return $path;
                }

        }

	/*****************************************************************
	 * Given a path to a raw file, find where the associated XMP
	 * sidecar should be located.  Usually in the same directory with
	 * the same filename except .xmp as the extension
         *****************************************************************/
	function _getSideCarPath($full_path) {
	global $CONFIG;

		$data = $this->splitMediaFilename($full_path);
		$filename_noextension = $data['filename_noextension'];
		$xmp_file = $filename_noextension.'.xmp';

		$last_slash = strrpos($full_path,'/');
		$path = substr($full_path,0,$last_slash);

		$full_xmp_file = $path.'/'.$xmp_file;

		#Remove the media path if it exists
		$full_xmp_file = str_replace($CONFIG['media_path'],'',$full_xmp_file);

		#Now, put it back in
		$full_xmp_file = $CONFIG['media_path'].'/'.$full_xmp_file;

		#Remove double slashes
		$full_xmp_file = str_replace('//','/',$full_xmp_file);

		return $full_xmp_file;
	}

	/*****************************************************************
	 * Given a full file's path, this extracts the relative path
	 * that should be stored in the database.
         *****************************************************************/
	function getRelativePath($full_path) {
	global $CONFIG;

		$last_slash = strrpos($full_path,'/');
		$path = substr($full_path,0,$last_slash);
		$path_relative = str_replace($CONFIG['media_path'],'',$path);
		$path_relative = $path_relative.'/';

		return $path_relative;
	}

	/*****************************************************************
	 * This function will extract as much information from the photo
	 * as possible and return it in an array
         *****************************************************************/
	function _scanPhoto($full_path,$photo_md5=false,$photo_id=false) {
	global $CONFIG;
	global $db;

		# First, find the relative path ... this is the path
                # of the file from a directory above the ORIGINALS/MODIFIED
                # directory, up to the filename.
                $path_relative = $this->getRelativePath($full_path);

		# Find the filename, filename_extension, filename_noextension
                # From the given full_path
                $filename_array = $this->splitMediaFilename($full_path);
                $filename_extension = $filename_array['filename_extension'];
                $filename_noextension = $filename_array['filename_noextension'];
                $filename = $filename_array['filename'];

		# From the extension we just found, is this even a file we
		# should be scanning?
		if (!in_array(strtolower($filename_extension),$CONFIG['acceptable_media'])) {
			return false;
		}
		
		#If there's no photo_md5, generate it.
		if (!$photo_md5) {
			$photo_md5 = md5_file($full_path);
		}

		#If there's no photo_id, generate it.
		if (!$photo_id) {
			$photo_id = $this->getPhotoID($full_path);
		}

		# If a modified version of the scanned file exists,
		# grab the creation and modified dates.  Also set the
		# path for the modified file which we'll use as an
		# emergency backup if no EXIF tags could be read from
		# the original file.					
		if ($this->modifiedExists($full_path)) {
			$timestamp_modified_ctime = filectime($modified_path);
			$timestamp_modified_mtime = filemtime($modified_path);
			$full_path_modified = $this->getModifiedFile($full_path);
		} else {
			$timestamp_modified_ctime = 0;
			$timestamp_modified_mtime = 0;
			$full_path_modified = NULL;
		}

		# Get the current time.
		$time = time();

		#Assume Title from Filename
		$title_period_position = strpos($filename,'.');
		$title = substr($filename,0,$title_period_position);

		#Get Actual Image Details: width, height
		$get_image_details = getimagesize($full_path);
		list($width, $height, $type, $attr) = $get_image_details;
		$mime = $get_image_details['mime'];
		$bits = $get_image_details['bits'];
		$channels = $get_image_details['channels'];

		#Get Filesize
		$filesize_bytes = filesize($full_path);
		$filesize_kilobytes = $filesize_bytes / 1024;
		$filesize_megabytes = $filesize_kilobytes / 1024;

		####################################################################################################################################
		# ALBUM GENERATIOIN PART -- SHOULD PROBABLY GO TO THE ALBUM CLASS
		####################################################################################################################################
		# Get Album Name from the containing folder of the
		# Scanned file.  Also, if the first 10 characters
		# of the folder name happen to be a date, use this
		# as the creation date of the album.
		$last_slash_position = strrpos($full_path,'/');
		$album_string = substr($full_path,0,$last_slash_position);
		$last_slash_position = strrpos($album_string,'/');
		$album = substr($album_string,$last_slash_position+1);
		$album_date = substr($album,0,10);
		if (is_numeric(str_replace('-','',$album_date))) {
			$album_timestamp = strtotime($album_date);
		} else {
			$album_timestamp = $time;
		}

		#All scanned folders are rolls
		$album_type = 1;

		#Generate clean album title
                $album_title_url = Text::generateCleanTitle($album);

		# CREATE THE ALBUM IF NECESSARY
		# First, check if the album has been quieried from the
		# database.  This will save us from doing an unnecessary
		# extra query if importing in bulk.
		if ($this->album_list[$album]) {
			$album_id = $this->album_list[$album];

		#If now, first try to select the album from the database;
		#If it doesn't exist, create the records
		} else {
			$query = "SELECT album_id
				  FROM ".$CONFIG['tbl_albums']." a
				  WHERE title = '".addslashes($album)."'";
			$row = $db->getRow($query);
			
			if ($row['album_id']){
				$album_id = $row['album_id'];
				$this->album_list[$album] = $album_id;
			#Create the records
			} else {
				$album_id = md5($album);

				$query = "INSERT INTO ".$CONFIG['tbl_albums']."
					  (album_id,title,title_url,timestamp_created,timestamp_modified,num_photos,type,highlight_photo_id)
					  VALUES ('".$album_id."',
						  '".addslashes($album)."',
						  '".addslashes($album_title_url)."',
						  '".$album_timestamp."',
						  '".$time."',
						  '0',
						  '".$album_type."',
						  '$photo_id')";
				$result = $db->Execute($query);
				if ($result){
					$this->album_list[$album] = $album_id;
				}
			}
		}

		$scanned_media_info['album_id'] = $album_id;
		$scanned_media_info['album_title_url'] = $album_title_url;

		####################################################################################################################################

		#Set basic info
		$scanned_media_info['title'] = $title;
                $scanned_media_info['filename'] = $filename;
                $scanned_media_info['path'] = $path_relative;
		$scanned_media_info['filename_extension'] = $filename_extension;
                $scanned_media_info['filename_noextension'] = $filename_noextension;

		#At this point, we need a photo_id to continue.
		#This is enough data to do a simple album
		#path swap 
		if (!$photo_id) {

			#Find the photo_id
			$photo_id = $this->getPhotoID($full_path);
			$scanned_media_info['photo_id'] = $photo_id;

			return $scanned_media_info;
		} 

		#######################################################################################	
		# Query EXIF Tag data from Files.
		#######################################################################################

		# If a photo
		if (!in_array(strtolower($filename_extension),$CONFIG['video_files'])) {
			$exif = exif_read_data($full_path,'IFD0,COMMENT,EXIF',true);
			if (empty($exif['EXIF'])) {
				$exif = exif_read_data($full_path_modified,'IFD0,COMMENT,EXIF',true);
			}
			$is_video = 0;
			$scanned_media_info['EXIF_DateTimeOriginal'] = $exif['EXIF']['DateTimeOriginal'];
			$scanned_media_info['EXIF_DateTimeDigitized'] = $exif['EXIF']['DateTimeDigitized'];
		}

		# If a video
		else if (in_array(strtolower($filename_extension),$CONFIG['video_files'])) {
			$is_video = 1;
			$video_tags = $this->getVideoTags($full_path);

			$scanned_media_info['EXIF_DateTimeOriginal'] = $video_tags['datetime_tagged']; 
			$scanned_media_info['EXIF_DateTimeDigitized'] = $video_tags['datetime_encoded'];
		}
		#######################################################################################



		#######################################################################################
		# Now, Set the scanned_media_info data
		#######################################################################################

		# Is this a video?
		$scanned_media_info['is_video'] = $is_video;

		#Set photo_id and photo_md5
		$scanned_media_info['photo_id'] = $photo_id;
		$scanned_media_info['photo_md5'] = $photo_md5;

		#Set EXIF Data
		$scanned_media_info['EXIF_Make'] = $exif['IFD0']['Make'];
		$scanned_media_info['EXIF_Model'] = $exif['IFD0']['Model'];
		$scanned_media_info['EXIF_Orientation'] = $exif['IFD0']['Orientation'];
		$scanned_media_info['EXIF_Software'] = $exif['IFD0']['Software'];
		$scanned_media_info['EXIF_DateTime'] = $exif['IFD0']['DateTime'];
		$scanned_media_info['EXIF_HostComputer'] = $exif['IFD0']['HostComputer'];
		$scanned_media_info['EXIF_FNumber'] = Text::divideBySlash($exif['EXIF']['FNumber']);
		$scanned_media_info['EXIF_ExposureProgram'] = $exif['EXIF']['ExposureProgram'];
		$scanned_media_info['EXIF_ISOSpeedRatings'] = $exif['EXIF']['ISOSpeedRatings'];
		$scanned_media_info['EXIF_CompressedBitsPerPixel'] = $exif['EXIF']['CompressedBitsPerPixel'];
		$scanned_media_info['EXIF_ShutterSpeedValue'] = $exif['EXIF']['ShutterSpeedValue'];
		$scanned_media_info['EXIF_ApertureValue'] = Text::divideBySlash($exif['EXIF']['ApertureValue']);
		$scanned_media_info['EXIF_BrightnessValue'] = $exif['EXIF']['BrightnessValue'];
		$scanned_media_info['EXIF_ExposureBiasValue'] = $exif['EXIF']['ExposureBiasValue'];
		$scanned_media_info['EXIF_MaxApertureValue'] = $exif['EXIF']['MaxApertureValue'];
		$scanned_media_info['EXIF_MeteringMode'] = $exif['EXIF']['MeteringMode'];
		$scanned_media_info['EXIF_Flash'] = $exif['EXIF']['Flash'];
		$scanned_media_info['EXIF_FocalLength'] = Text::divideBySlash($exif['EXIF']['FocalLength']);

		#Set other Data
		$scanned_media_info['height'] = $height;
		$scanned_media_info['width'] = $width;
		$scanned_media_info['type'] = $type;
		$scanned_media_info['bits'] = $bits;
		$scanned_media_info['mime'] = $mime;
		$scanned_media_info['channels'] = $channels;
		$scanned_media_info['filesize_bytes'] = $filesize_bytes;
		$scanned_media_info['filesize_kilobytes'] = $filesize_kilobytes;
		$scanned_media_info['filesize_megabytes'] = $filesize_megabytes;
		$scanned_media_info['timestamp_original_ctime'] = $time;

		#######################################################################################	
		#Read IPTC Data from Photo
		#######################################################################################
		if (!in_array(strtolower($filename_extension),$CONFIG['video_files'])) {
			$size = getImageSize($full_path,&$info);  
			$iptc = iptcparse ($info['APP13']);

			if (isset($info['APP13'])) {
				$iptc = iptcparse($info['APP13']);
				if (is_array($iptc)) { 
					$title = trim($iptc['2#005'][0]);        
					$tag_array = $iptc['2#025'];
					$tag_list = implode(', ',$tag_array);
					$description = trim($iptc['2#120'][0]);       
				}

				$scanned_media_info['tag_list'] = addslashes($tag_list);

				if (!empty($title)) {
					$scanned_media_info['title'] = $title;
				}
				$scanned_media_info['text'] = $description;
			}

			#Attempt to fetch an embedded rating if it's a photo
			$source = file_get_contents($full_path);
			$xmpdata_start = strpos($source,"<x:xmpmeta");
			$xmpdata_end = strpos($source,"</x:xmpmeta>");
			$xmplenght = $xmpdata_end-$xmpdata_start;
			$xmpdata = substr($source,$xmpdata_start,$xmplenght+12);
			$rating_pos = strpos($xmpdata,"xap:Rating=\"");
			$rating = substr($xmpdata,$rating_pos+12,1);

			if (is_numeric($rating) AND $rating >= 0 AND $rating <= 5) {
				$scanned_media_info['rating'] = $rating;
			}
		}
		#######################################################################################



		#######################################################################################
		#Find an XMP sidecar if it exists, read the various information from it
		#And associate it with the photo.
		#######################################################################################
		$xmp_file = $this->_getSideCarPath($full_path);
		if (file_exists($xmp_file)) {
		
			#Insert MD5 Hash of sidecar into database
			$scanned_media_info['sidecar_md5'] = md5_file($xmp_file);
	
			$xml = new XML($xmp_file);
			$xml_out = $xml->XMLToArray();
			
			#Scan through the xmp file and catch the desired values:
			#TAGS
			#RATING
			#PICK/REJECTED
			#TITLE
			#DESCRIPTION
			for ($i=0; $i<count($xml_out['x:xmpmeta']['rdf:RDF']['rdf:Description']); $i++) {

				#Grab the specified tags
				$tag_key = $xml_out['x:xmpmeta']['rdf:RDF']['rdf:Description'][$i]['lr:hierarchicalSubject']['rdf:Bag']['rdf:li'];
				if ($tag_key) {
					$tag_array = $tag_key;
				}

				#Grab the 1-5 Rating
				$rating_key = $xml_out['x:xmpmeta']['rdf:RDF']['rdf:Description'][$i]['xap:Rating'];
				if (is_numeric($rating_key) AND $rating_key <= 5 AND $rating_key >= 0 ) {
					$rating = $xml_out['x:xmpmeta']['rdf:RDF']['rdf:Description'][$i]['xap:Rating'];
				}

				#Grab the title
				$title_key = $xml_out['x:xmpmeta']['rdf:RDF']['rdf:Description'][$i]['dc:title']['rdf:Alt']['rdf:li'];
				if ($title_key) {
					$title = $title_key;
				}

				#Grab the description
				$description_key = $xml_out['x:xmpmeta']['rdf:RDF']['rdf:Description'][$i]['dc:description']['rdf:Alt']['rdf:li'];
				if ($description_key) {
					$description = $description_key;
				}
			}

			$tag_list = implode(', ',$tag_array);
			#Set Tag List
			if (isset($tag_list)) {
				$scanned_media_info['tag_list'] = addslashes($tag_list);
			}

			if (is_numeric($rating) AND $rating >= 0 AND $rating <= 5) {
				$scanned_media_info['rating'] = $rating;
			}

			if (isset($title)) {
				$scanned_media_info['title'] = trim($title);
			}

			if (isset($description)) {
				$scanned_media_info['text'] = trim($description);
			}

		}
		#######################################################################################

		return $scanned_media_info;
	}

	/*****************************************************************
	 * Flush output buffer
         *****************************************************************/
	function flushBuffer() {
		#Flush output buffer if print is enabled.                                       
		print str_pad("",4096)."\n";
		ob_flush();
		flush();
	}

	/*****************************************************************
         * Get Image Info
         *****************************************************************/
	function _getInfo($album_title_url=false,$photo_title_url=false,$cache=0,$sort=false,$full=false) {
	global $CONFIG;
        global $db;
	global $_USER;

		if ($album_title_url AND $photo_title_url) {
                	$query = "SELECT p.*,
					 p.title as photo_title,
					 a.title as album_title,
					 a.title_url as album_title_url,
                                 	 p.title_url as photo_title_url,
					 p.views as photo_views,
					 p.text as text,
					 p.tag_list as tag_list,
					 a.views as album_views,
					 a.type as type,
					 p.is_video as is_video,
					 a.num_photos as num_photos
                          	  FROM ".$CONFIG['tbl_photos']." p,
				       ".$CONFIG['tbl_albums']." a,
				       ".$CONFIG['tbl_album_photo_map']." apm
                          	  WHERE p.photo_id = apm.photo_id AND
				        apm.album_id = a.album_id AND
					( p.title_url = '$photo_title_url' OR p.title_url_previous = '$photo_title_url' ) AND
					a.title_url = '$album_title_url' AND
					a.level <= ".$_USER['level']." AND
					p.level <= ".$_USER['level']."";
		} else {
			if ($full AND $album_title_url) {
				$query = "SELECT p.*,
                                         p.title as photo_title,
                                         a.title as album_title,
                                         a.title_url as album_title_url,
                                         p.title_url as photo_title_url,
                                         p.views as photo_views,
                                         p.text as text,
                                         p.tag_list as tag_list,
                                         a.views as album_views,
                                         a.type as type,
                                         p.is_video as is_video
                                  FROM ".$CONFIG['tbl_photos']." p,
                                       ".$CONFIG['tbl_albums']." a,
                                       ".$CONFIG['tbl_album_photo_map']." apm
                                  WHERE p.photo_id = apm.photo_id AND
                                        apm.album_id = a.album_id AND
					p.photo_id = '".$this->photo_id."' AND
					a.album_id = '".$album_title_url."' AND
                                        a.level <= ".$_USER['level']." AND
                                        p.level <= ".$_USER['level']."";

			} else {

				if (isset($this->photo_id)) {
					$where = "photo_id = '".$this->photo_id."'";
				} elseif (isset($this->photo_md5)) {
					$where = "photo_md5 = '".$this->photo_md5."'";
				}

				$query = "SELECT filename,
						 filename_extension,
						 filename_noextension,
						 path,
						 is_video,
						 level,
						 title_url,
						 tag_list,
						 photo_id,
						 photo_md5,
						 EXIF_Orientation
					  FROM ".$CONFIG['tbl_photos']."
					  WHERE $where AND
						level <= ".$_USER['level']."";
			}
		}
                $row = $db->CacheGetRow($cache,$query);

		if ($row){

			#Determine next and previous photos
			if ($album_title_url AND $photo_title_url) {
				$photo_list = $this->getPhotoList($album_title_url,false,false,false,$sort);
				for ($i=0; $i<count($photo_list); $i++){
					if ($photo_list[$i]['photo_title_url'] == $photo_title_url OR
					    $photo_list[$i]['photo_title_url_previous'] == $photo_title_url ) {

						if ($sort !== '') {
							$sort_addition = '/&sort='.$sort;
						} else {
							$sort_addition = '';
						}

						if ($photo_list[$i+1]){
							$row['preview_url_next'] = $photo_list[$i+1]['preview_url'].$sort_addition;
						}

						if (isset($photo_list[$i-1]) AND $photo_list[$i-1]) {
							$row['preview_url_previous'] = $photo_list[$i-1]['preview_url'].$sort_addition;
						}

						if (isset($photo_list[$i+2]) AND $photo_list[$i+2]) {
							$row['preview_url_next_next'] = $photo_list[$i+2]['preview_url'].$sort_addition;
						}

						if (isset($photo_list[$i-2]) AND $photo_list[$i-2]) {
                	                                $row['preview_url_previous_previous'] = $photo_list[$i-2]['preview_url'].$sort_addition;
						}

						if (isset($photo_list[$i+1]) AND $photo_list[$i+1]) {
							$row['url_next'] = $CONFIG['full_site'].$photo_list[$i+1]['album_title_url'].'/'.$photo_list[$i+1]['photo_title_url'].$sort_addition;
						}

						if (isset($photo_list[$i-1]) AND $photo_list[$i-1]) {
							$row['url_previous'] = $CONFIG['full_site'].$photo_list[$i-1]['album_title_url'].'/'.$photo_list[$i-1]['photo_title_url'].$sort_addition;
						}

						if (isset($photo_list[$i+2]) AND $photo_list[$i+2]) {
							$row['url_next_next'] = $CONFIG['full_site'].$photo_list[$i+1]['album_title_url'].'/'.$photo_list[$i+2]['photo_title_url'].$sort_addition;
						}

						if (isset($photo_list[$i-2]) AND $photo_list[$i-2]) {
                                                	$row['url_previous_previous'] = $CONFIG['full_site'].$photo_list[$i-1]['album_title_url'].'/'.$photo_list[$i-2]['photo_title_url'].$sort_addition;
						}

					}
				}
			}

			if (empty($this->photo_id)){
				$this->photo_id = $row['photo_id'];
			}

			$row['micro_url'] = $CONFIG['full_site'].'photo/'.$row['photo_id'].'-micro.'.$row['filename_extension'];
                        $row['thumb_url'] = $CONFIG['full_site'].'photo/'.$row['photo_id'].'-thumb.'.$row['filename_extension'];
                        $row['preview_url'] = $CONFIG['full_site'].'photo/'.$row['photo_id'].'-preview.jpg';
                        $row['full_url'] = $CONFIG['full_site'].'photo/'.$row['photo_id'].'-full.'.$row['filename_extension'];
			$row['download_url'] = $CONFIG['full_site'].'photo/'.$row['photo_id'].'-download.'.$row['filename_extension'];
			$row['video_url'] = $CONFIG['full_site'].'photo/'.$row['photo_id'].'-video.mov';

			if (in_array(strtolower($row['filename_extension']),$CONFIG['video_files'])) {
        	                $row['is_video'] = TRUE;
                        } else {
   	                        $row['is_video'] = FALSE;
                        }

			if (isset($row['title'])) {
				if ( in_array(strtolower(substr($row['title'],0,3)),$CONFIG['default_prefix']) OR
				     in_array(strtolower(substr($row['title'],0,8)),$CONFIG['default_prefix']) ) {
					$row['untitled'] = true;
				}
			}
			
			return $row;
		} else {
			return false;
		}
	}
}
