<?php /*

 ocPortal
 Copyright (c) ocProducts, 2004-2012

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/
/*EXTRA FUNCTIONS: shell_exec*/

/**
 * @license		http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright	ocProducts Ltd
 * @package		galleries
 */

/**
 * Get width,height,length of a video file. Note: unfortunately mpeg is not possible without huge amounts of code.
 *
 * @param  PATH			The path to the video file
 * @param  string			The original filename of the video file (so we can find the file type from the file extension)
 * @param  boolean		Whether to skip over errored files instead of dying. We don't currently make use of this as our readers aren't sophisticard enough to properly spot erroneous situations.
 * @return ~array			The triplet of width/height/length (possibly containing NULL's for when we can't detect properties) (false: error)
 */
function get_video_details($file_path,$filename,$delay_errors=false)
{
	unset($delay_errors);

	$info=NULL;

	$extension=get_file_extension($filename);

	$file=@fopen($file_path,'rb');
	if ($file===false) return false;

	switch ($extension)
	{
		case 'wmv':
			$info=_get_wmv_details($file);
			break;
		case 'asf':
			$info=_get_wmv_details($file);
			break;
		case 'avi':
			$info=_get_avi_details($file);
			break;
		case 'rm':
			$info=_get_ram_details($file);
			break;
		case 'ram':
			$info=_get_ram_details($file);
			break;
		case 'qt':
			$info=_get_mov_details($file);
			break;
		case 'mov':
			$info=_get_mov_details($file);
			break;
		case 'mp4':
			$info=_get_mov_details($file);
			break;
		case 'm4v':
			$info=_get_mov_details($file);
			break;
		default:
			if (file_exists(get_file_base().'/sources_custom/getid3/getid3.php'))
			{
				error_reporting(0);
				
				if (!defined('GETID3_HELPERAPPSDIR')) define('GETID3_HELPERAPPSDIR',get_file_base().'/sources_custom/getid3/helperapps');

				require_code('getid3/getid3');
				if (class_exists('getID3'))
				{
					$id3_ob=new getID3();
					$_info=$id3_ob->analyze($file_path,$filename);
					$info=array(
						isset($_info['video']['resolution_x'])?$_info['video']['resolution_x']:NULL,
						isset($_info['video']['resolution_y'])?$_info['video']['resolution_y']:NULL,
						array_key_exists('playtime_seconds',$_info)?intval($_info['playtime_seconds']):NULL,
					);
					if (isset($_info['meta']['onMetaData']['width'])) $info[0]=intval($_info['meta']['onMetaData']['width']);
					if (isset($_info['meta']['onMetaData']['height'])) $info[1]=intval($_info['meta']['onMetaData']['height']);

					require_code('mime_types');
					$mime_type=get_mime_type($extension);
					if (substr($mime_type,0,6)=='audio/')
					{
						$info[0]=NULL;
						$info[1]=NULL;
					}
				}
			}
			break;
	}

	fclose($file);

	if (is_null($info)) return array(NULL,NULL,NULL);
	return $info;
}

/**
 * Read an integer from the given binary chunk. The integer is in intel endian form.
 *
 * @param  string			The binary chunk
 * @return integer		The integer
 */
function read_intel_endian_int($buffer)
{
	if (strlen($buffer)==2)
	{
		return ord($buffer[0])|(ord($buffer[1])<<8);
	}
	if (strlen($buffer)<4) warn_exit(do_lang_tempcode('CORRUPT_FILE',do_lang('VIDEO'))); // Error
	return ord($buffer[0])|(ord($buffer[1])<<8)|(ord($buffer[2])<<16)|(ord($buffer[3])<<24);
}

/**
 * Read an integer from the given binary chunk. The integer is in network endian form.
 *
 * @param  string			The binary chunk
 * @return integer		The integer
 */
function read_network_endian_int($buffer)
{
	if (strlen($buffer)==2)
	{
		return ord($buffer[1])|(ord($buffer[0])<<8);
	}
	if (strlen($buffer)<4) warn_exit(do_lang_tempcode('CORRUPT_FILE',do_lang('VIDEO'))); // Error
	return ord($buffer[3])|(ord($buffer[2])<<8)|(ord($buffer[1])<<16)|(ord($buffer[0])<<24);
}

/**
 * Get width,height,length of a .wmv video file.
 *
 * @param  resource		The file handle
 * @return array			The triplet (possibly containing NULL's for when we can't detect properties)
 */
function _get_wmv_details($file)
{
	// Read in chunks
	list($_,$width,$height,$length)=_get_wmv_details_do_chunk_list($file);
	return array($width,$height,$length);
}

/**
 * Get chunk-bytes-read,width,height,length of a chunk list of a .wmv video file.
 *
 * @param  resource		The file handle
 * @param  ?integer		The length of the current chunk list (NULL: covers full file)
 * @return ?array			The quartet (possibly containing NULL's for when we can't detect properties) (NULL: error)
 */
function _get_wmv_details_do_chunk_list($file,$chunk_length=NULL)
{
	$length=NULL;
	$width=NULL;
	$height=NULL;

	$count=0;
	while ((!feof($file)) && ((is_null($chunk_length)) || ($count<$chunk_length)) && ((is_null($length)) || (is_null($width)) || (is_null($height))))
	{
		// Read in chunk info
		$a=read_intel_endian_int(fread($file,4));
		$b=read_intel_endian_int(fread($file,4));
		$c=read_intel_endian_int(fread($file,4));
		$d=read_intel_endian_int(fread($file,4));
		$sub_chunk_length=read_intel_endian_int(fread($file,4));
		$count+=$sub_chunk_length;
		if ($sub_chunk_length<=24) return NULL; // Some kind of error that would cause mayhem
		fseek($file,4,SEEK_CUR); // Can't read 64 bit

		// Header chunk
		if (($a==intval(0x75B22630)) && ($b==intval(0x11CF668E)) && ($c==intval(0xAA00D9A6)) && ($d==intval(0x6CCE6200)))
		{
			fseek($file,6,SEEK_CUR);
			$info=_get_wmv_details_do_chunk_list($file,$sub_chunk_length-30);
			$sub_chunk_length=24;
			if (!is_null($info[1])) $width=$info[1];
			if (!is_null($info[2])) $height=$info[2];
			if (!is_null($info[3])) $length=$info[3];
		}

		// Header object
		if (($a==intval(0x8CABDCA1)) && ($b==intval(0x11CFA947)) && ($c==intval(0xC000E48E)) && ($d==intval(0x6553200C)))
		{
			fseek($file,48,SEEK_CUR);
			$length=intval(round(read_intel_endian_int(fread($file,4))/10000000));
			$sub_chunk_length-=52;
		}

		// Stream header object
		if (($a==intval(0xB7DC0791)) && ($b==intval(0x11CFA9B7)) && ($c==intval(0xC000E68E)) && ($d==intval(0x6553200C)))
		{
			// Read in chunk info
			$a=read_intel_endian_int(fread($file,4));
			$b=read_intel_endian_int(fread($file,4));
			$c=read_intel_endian_int(fread($file,4));
			$d=read_intel_endian_int(fread($file,4));
			$sub_chunk_length-=16;
			if (($a==intval(0xBC19EFC0)) && ($b==intval(0x11CF5B4D)) && ($c==intval(0x8000FDA8)) && ($d==intval(0x2B445C5F))) // Video chunk
			{
				fseek($file,38,SEEK_CUR);
				$width=read_intel_endian_int(fread($file,4));
				$height=read_intel_endian_int(fread($file,4));
				$sub_chunk_length-=46;
			}
		}
		fseek($file,$sub_chunk_length-24,SEEK_CUR);
	}

	return array($count,$width,$height,$length);
}

/**
 * Get width,height,length of a .avi video file.
 *
 * @param  resource		The file handle
 * @return array			The triplet (possibly containing NULL's for when we can't detect properties)
 */
function _get_avi_details($file)
{
	fseek($file,32,SEEK_CUR);
	$microseconds_per_frame=read_intel_endian_int(fread($file,4));
	fseek($file,12,SEEK_CUR);
	$num_frames=read_intel_endian_int(fread($file,4));
	$length=intval(round(floatval($num_frames*$microseconds_per_frame)/1000/1000));
	fseek($file,12,SEEK_CUR);
	$width=read_intel_endian_int(fread($file,4));
	$height=read_intel_endian_int(fread($file,4));
	return array($width,$height,$length);
}

/**
 * Get width,height,length of a .rm/.ram video file.
 *
 * @param  resource		The file handle
 * @return ?array			The triplet (possibly containing NULL's for when we can't detect properties) (NULL: error)
 */
function _get_ram_details($file) // + rm
{
	$length=NULL;
	$width=NULL;
	$height=NULL;

	// Read in chunks
	while ((!feof($file)) && ((is_null($length)) || (is_null($width)) || (is_null($height))))
	{
		$type=fread($file,4);
		$size=read_network_endian_int(fread($file,4));
		if ($size<=8) return NULL;

		if ($type=='PROP')
		{
			fseek($file,22,SEEK_CUR);
			$length=intval(round(read_network_endian_int(fread($file,4))/1000));
			return array($width,$height,$length);
		} else fseek($file,$size-8,SEEK_CUR);
	}

	return array($width,$height,$length);
}

/**
 * Get width,height,length of a .mov/.qt video file.
 *
 * @param  resource		The file handle
 * @return ?array			The triplet (possibly containing NULL's for when we can't detect properties) (NULL: error)
 */
function _get_mov_details($file)
{
	// Read in atoms
	$info=_get_mov_details_do_atom_list($file);
	if (is_null($info)) return NULL;
	list($_,$width,$height,$length)=$info;
	return array($width,$height,$length);
}

/**
 * Get chunk-bytes-read,width,height,length of a atom list of a .mov/.qt video file.
 *
 * @param  resource		The file handle
 * @param  ?integer		The length of the current atom list (NULL: covers full file)
 * @return array			The quartet (possibly containing NULL's for when we can't detect properties)
 */
function _get_mov_details_do_atom_list($file,$atom_size=NULL)
{
	$length=NULL;
	$width=NULL;
	$height=NULL;

	$count=0;
	while ((!feof($file)) && ((is_null($atom_size)) || ($count<$atom_size)) && ((is_null($length)) || (is_null($width)) || (is_null($height))))
	{
		$next_read=fread($file,4);
		if (strlen($next_read)<4) return array($count,$width,$height,$length); // END / problem
		$size=read_network_endian_int($next_read);
		if ($size<8) // NB: uuid atom can be of size 8 (i.e. empty) on some rare files
		{
			return array($count,$width,$height,$length); // END / problem
		}
		$count+=4;
		if ($size==0)
		{
//			$qt_atom=true;
			fseek($file,8,SEEK_CUR);
			$size=read_network_endian_int(fread($file,4));
			$count+=12;
		}// else $qt_atom=false;

		$type=fread($file,4);
		$count+=4;
		if ($type=='mvhd')
		{
			fseek($file,12,SEEK_CUR);
			$time_scale=read_network_endian_int(fread($file,4));
			$duration=read_network_endian_int(fread($file,4));
			$length=intval(round(floatval($duration)/floatval($time_scale)));
			fseek($file,80,SEEK_CUR);
			$count+=20+80;
		}
		elseif ($type=='tkhd')
		{
			fseek($file,76,SEEK_CUR);
			$_width=read_network_endian_int(fread($file,2));
			fseek($file,2,SEEK_CUR); // fixed point - but we don't want decimal fraction part
			$_height=read_network_endian_int(fread($file,2));
			fseek($file,2,SEEK_CUR); // fixed point - but we don't want decimal fraction part
			$count+=76+8;
			if ($_width>0) $width=$_width;
			if ($_height>0) $height=$_height;
		}
		elseif ($type=='moov') // moov contains more atoms, and the one we need for length
		{
			$info=_get_mov_details_do_atom_list($file,$size-$count);
			$count+=$info[0];
			if (!is_null($info[1])) $width=$info[1];
			if (!is_null($info[2])) $height=$info[2];
			if (!is_null($info[3])) $length=$info[3];
		}
		elseif ($type=='trak') // trak contains more atoms, and the one we need for width and height
		{
			$info=_get_mov_details_do_atom_list($file,$size-$count);
			$count+=$info[0];
			if (!is_null($info[1])) $width=$info[1];
			if (!is_null($info[2])) $height=$info[2];
			if (!is_null($info[3])) $length=$info[3];
		} else
		{
			fseek($file,$size-8,SEEK_CUR);
			$count+=$size-8;
		}
	}

	return array($count,$width,$height,$length);
}

/**
 * Add an image to a specified gallery.
 *
 * @param  SHORT_TEXT	Image title
 * @param  ID_TEXT		The gallery name
 * @param  LONG_TEXT		The image comments
 * @param  URLPATH		The URL to the actual image
 * @param  URLPATH		The URL to the thumbnail of the actual image
 * @param  BINARY			Whether the image has been validated for display on the site
 * @param  BINARY			Whether the image may be rated
 * @param  BINARY			Whether the image may be commented upon
 * @param  BINARY			Whether the image may be trackbacked
 * @param  LONG_TEXT		Hidden notes associated with the image
 * @param  ?MEMBER		The submitter (NULL: current member)
 * @param  ?TIME			The time of adding (NULL: now)
 * @param  ?TIME			The time of editing (NULL: never)
 * @param  integer		The number of views
 * @param  ?AUTO_LINK	Force an ID (NULL: don't force an ID)
 * @return AUTO_LINK		The ID of the new entry
 */
function add_image($title,$cat,$comments,$url,$thumb_url,$validated,$allow_rating,$allow_comments,$allow_trackbacks,$notes,$submitter=NULL,$add_date=NULL,$edit_date=NULL,$views=0,$id=NULL)
{
	if (is_null($submitter)) $submitter=get_member();
	if (is_null($add_date)) $add_date=time();

	if (!addon_installed('unvalidated')) $validated=1;
	$map=array('title'=>insert_lang_comcode($title,2),'edit_date'=>$edit_date,'image_views'=>$views,'add_date'=>$add_date,'allow_rating'=>$allow_rating,'allow_comments'=>$allow_comments,'allow_trackbacks'=>$allow_trackbacks,'notes'=>$notes,'submitter'=>$submitter,'url'=>$url,'thumb_url'=>$thumb_url,'comments'=>insert_lang_comcode($comments,3),'cat'=>$cat,'validated'=>$validated);
	if (!is_null($id)) $map['id']=$id;
	$id=$GLOBALS['SITE_DB']->query_insert('images',$map,true);

	log_it('ADD_IMAGE',strval($id),$title);

	require_code('seo2');
	seo_meta_set_for_implicit('image',strval($id),array($comments),$comments);

	if ($validated==1)
	{
		require_lang('galleries');
		require_code('notifications');
		$subject=do_lang('IMAGE_NOTIFICATION_MAIL_SUBJECT',get_site_name(),strip_comcode($title));
		$self_url=build_url(array('page'=>'galleries','type'=>'image','id'=>$id),get_module_zone('galleries'),NULL,false,false,true);
		$mail=do_lang('IMAGE_NOTIFICATION_MAIL',comcode_escape(get_site_name()),comcode_escape($title),array(comcode_escape($self_url->evaluate())));
		dispatch_notification('gallery_entry',$cat,$subject,$mail);
	}

	decache('side_root_galleries');
	decache('main_gallery_embed');

	decache('main_download_category');
	decache('main_image_fader');

	return $id;
}

/**
 * Edit an image in a specified gallery.
 *
 * @param  AUTO_LINK		The ID of the image to edit
 * @param  SHORT_TEXT	Image title
 * @param  ID_TEXT		The gallery name
 * @param  LONG_TEXT		The image comments
 * @param  URLPATH		The URL to the actual image
 * @param  URLPATH		The URL to the thumbnail of the actual image
 * @param  BINARY			Whether the image has been validated for display on the site
 * @param  BINARY			Whether the image may be rated
 * @param  BINARY			Whether the image may be commented upon
 * @param  BINARY			Whether the image may be trackbacked
 * @param  LONG_TEXT		Hidden notes associated with the image
 * @param  SHORT_TEXT	Meta keywords
 * @param  LONG_TEXT		Meta description
 */
function edit_image($id,$title,$cat,$comments,$url,$thumb_url,$validated,$allow_rating,$allow_comments,$allow_trackbacks,$notes,$meta_keywords,$meta_description)
{
	require_code('urls2');
	suggest_new_idmoniker_for('galleries','image',strval($id),($title=='')?$comments:$title);

	$_comments=$GLOBALS['SITE_DB']->query_value('images','comments',array('id'=>$id));
	$_title=$GLOBALS['SITE_DB']->query_value('images','title',array('id'=>$id));

	decache('main_gallery_embed');

	require_code('files2');
	delete_upload('uploads/galleries','images','url','id',$id,$url);
	delete_upload('uploads/galleries_thumbs','images','thumb_url','id',$id,$thumb_url);

	if (!addon_installed('unvalidated')) $validated=1;

	require_code('submit');
	$just_validated=(!content_validated('image',strval($id))) && ($validated==1);
	if ($just_validated)
	{
		send_content_validated_notification('image',strval($id));
	}

	$GLOBALS['SITE_DB']->query_update('images',array('title'=>lang_remap_comcode($_title,$title),'edit_date'=>time(),'allow_rating'=>$allow_rating,'allow_comments'=>$allow_comments,'allow_trackbacks'=>$allow_trackbacks,'notes'=>$notes,'validated'=>$validated,'cat'=>$cat,'comments'=>lang_remap_comcode($_comments,$comments),'url'=>$url,'thumb_url'=>$thumb_url),array('id'=>$id),'',1);

	$self_url=build_url(array('page'=>'galleries','type'=>'image','id'=>$id),get_module_zone('galleries'),NULL,false,false,true);

	if ($just_validated)
	{
		require_lang('galleries');
		require_code('notifications');
		$subject=do_lang('IMAGE_NOTIFICATION_MAIL_SUBJECT',get_site_name(),strip_comcode($title));
		$mail=do_lang('IMAGE_NOTIFICATION_MAIL',comcode_escape(get_site_name()),comcode_escape($title),array(comcode_escape($self_url->evaluate())));
		dispatch_notification('gallery_entry',$cat,$subject,$mail);
	}

	log_it('EDIT_IMAGE',strval($id),$title);

	require_code('seo2');
	seo_meta_set_for_explicit('image',strval($id),$meta_keywords,$meta_description);

	decache('main_download_category');
	decache('main_image_fader');

	require_lang('galleries');
	require_code('feedback');
	update_spacer_post($allow_comments!=0,'images',strval($id),$self_url,do_lang('VIEW_IMAGE','','','',get_site_default_lang()),get_value('comment_forum__images'));
}

/**
 * Delete a specified image from the database, and delete the file if possible.
 *
 * @param  AUTO_LINK		The ID of the image
 * @param  boolean		Whether to delete the actual file also
 */
function delete_image($id,$delete_full)
{
	$rows=$GLOBALS['SITE_DB']->query_select('images',array('title','comments','cat'),array('id'=>$id));
	$title=$rows[0]['title'];
	$comments=$rows[0]['comments'];
	$cat=$rows[0]['cat'];

	log_it('DELETE_IMAGE',strval($id),get_translated_text($title));
	delete_lang($comments);
	delete_lang($title);

	// Delete file
	if ($delete_full)
	{
		require_code('files2');
		delete_upload('uploads/galleries','images','url','id',$id);
		delete_upload('uploads/galleries_thumbs','images','thumb_url','id',$id);
	}

	// Delete from database
	$GLOBALS['SITE_DB']->query_delete('images',array('id'=>$id),'',1);
	$GLOBALS['SITE_DB']->query_delete('rating',array('rating_for_type'=>'images','rating_for_id'=>$id));
	$GLOBALS['SITE_DB']->query_delete('trackbacks',array('trackback_for_type'=>'images','trackback_for_id'=>$id));

	require_code('seo2');
	seo_meta_erase_storage('image',strval($id));

	decache('side_root_galleries');
	decache('main_gallery_embed');
	decache('main_image_fader');
}

/**
 * Create a video thumbnail.
 *
 * @param  URLPATH		Video to get thumbail from (must be local)
 * @param  ?PATH			Where to save to (NULL: decide for ourselves)
 * @return URLPATH		Thumbnail, only valid if expected_output_path was passed as NULL (blank: could not generate)
 */
function create_video_thumb($src_url,$expected_output_path=NULL)
{
	$ve_hooks=find_all_hooks('systems','video_embed');
	foreach (array_keys($ve_hooks) as $ve_hook)
	{
		require_code('hooks/systems/video_embed/'.$ve_hook);
		$ve_ob=object_factory('Hook_video_embed_'.$ve_hook);
		$thumbnail=$ve_ob->get_video_thumbnail($src_url);
		if (!is_null($thumbnail)) return $thumbnail;
	}

	if (substr($src_url,0,strlen(get_custom_base_url().'/'))==get_custom_base_url().'/') $src_url=substr($src_url,strlen(get_custom_base_url().'/'));
	if (!url_is_local($src_url)) return '';
	
	$src_file=get_custom_file_base().'/'.rawurldecode($src_url);
	$src_file=preg_replace('#(\\\|/)#',DIRECTORY_SEPARATOR,$src_file);
	
	if (class_exists('ffmpeg_movie'))
	{
		$filename='thumb_'.md5(uniqid('')).'1.jpg';
		if (is_null($expected_output_path))
			$expected_output_path=get_custom_file_base().'/uploads/galleries/'.$filename;
		if (file_exists($expected_output_path))
			return 'uploads/galleries/'.rawurlencode(basename($expected_output_path));

		$movie=@(new ffmpeg_movie($src_file,false));
		if ($movie!==false)
		{
			if ($movie->getFrameCount()==0) return '';

			$frame=$movie->getFrame(min($movie->getFrameCount(),25));
			$gd_img=$frame->toGDImage();

			@imagejpeg($gd_img,$expected_output_path);

			if (file_exists($expected_output_path))
			{
				require_code('images');
				if ((get_option('is_on_gd')=='1') && (function_exists('imagecreatefromstring')))
					convert_image($expected_output_path,$expected_output_path,-1,-1,intval(get_option('thumb_width')),true,NULL,true);

				return 'uploads/galleries/'.rawurlencode(basename($expected_output_path));
			}
		}
	}

	$ffmpeg_path=get_option('ffmpeg_path');

	if (($ffmpeg_path!='') && (strpos(@ini_get('disable_functions'),'shell_exec')===false))
	{
		$filename='thumb_'.md5(uniqid(strval(post_param_integer('thumbnail_auto_position',1)))).'%d.jpg';
		$dest_file=get_custom_file_base().'/uploads/galleries/'.$filename;
		if (is_null($expected_output_path))
			$expected_output_path=str_replace('%d','1',$dest_file);

		if ((file_exists($dest_file)) && (is_null(post_param_integer('thumbnail_auto_position',NULL))))
			return 'uploads/galleries/'.rawurlencode(basename($expected_output_path));
		@unlink($dest_file); // So "if (@filesize($expected_output_path)) break;" will definitely fail if error

		$dest_file=preg_replace('#(\\\|/)#',DIRECTORY_SEPARATOR,$dest_file);
		
		$at=display_seconds_period(post_param_integer('thumbnail_auto_position',1));
		if (strlen($at)==5) $at='00:'.$at;

		$shell_command='"'.$ffmpeg_path.'ffmpeg" -i '.@escapeshellarg($src_file).' -an -ss '.$at.' -r 1 -vframes 1 -y '.@escapeshellarg($dest_file);

		$shell_commands=array($shell_command,$shell_command.' -map 0.0:0.0',$shell_command.' -map 0.1:0.0');
		foreach ($shell_commands as $shell_command)
		{
			shell_exec($shell_command);
			if (@filesize($expected_output_path)) break;
		}

		if (file_exists(str_replace('%d','1',$dest_file)))
		{
			require_code('images');
			if ((get_option('is_on_gd')=='1') && (function_exists('imagecreatefromstring')))
			{
				convert_image(str_replace('%d','1',$dest_file),$expected_output_path,-1,-1,intval(get_option('thumb_width')),true,NULL,true);
			} else
			{
				copy(str_replace('%d','1',$dest_file),$expected_output_path);
				fix_permissions($expected_output_path);
				sync_file($expected_output_path);
			}

			return 'uploads/galleries/'.rawurlencode(basename($expected_output_path));
		}
	}
	
	// get_mime_type
	require_code('mime_types');
	$file_ext=get_file_extension($src_url);
	$input_mime_type=get_mime_type($file_ext);
	if (preg_match('#audio\/#i',$input_mime_type)!=0)
	{
		return find_theme_image('audio_thumb',true);
	}

	return '';
}

/**
 * Add a video to a specified gallery.
 *
 * @param  SHORT_TEXT	Video title
 * @param  ID_TEXT		The gallery name
 * @param  LONG_TEXT		The video comments
 * @param  URLPATH		The URL to the actual video
 * @param  URLPATH		The URL to the thumbnail of the actual video
 * @param  BINARY			Whether the video has been validated for display on the site
 * @param  BINARY			Whether the video may be rated
 * @param  BINARY			Whether the video may be commented upon
 * @param  BINARY			Whether the video may be trackbacked
 * @param  LONG_TEXT		Hidden notes associated with the video
 * @param  integer		The length of the video
 * @param  integer		The width of the video
 * @param  integer		The height of the video
 * @param  ?MEMBER		The submitter (NULL: current member)
 * @param  ?TIME			The time of adding (NULL: now)
 * @param  ?TIME			The time of editing (NULL: never)
 * @param  integer		The number of views
 * @param  ?AUTO_LINK	Force an ID (NULL: don't force an ID)
 * @return AUTO_LINK		The ID of the new entry
 */
function add_video($title,$cat,$comments,$url,$thumb_url,$validated,$allow_rating,$allow_comments,$allow_trackbacks,$notes,$video_length,$video_width,$video_height,$submitter=NULL,$add_date=NULL,$edit_date=NULL,$views=0,$id=NULL)
{
	if (is_null($submitter)) $submitter=get_member();
	if (is_null($add_date)) $add_date=time();

	require_code('transcoding');
	$url=transcode_video($url,'videos','url',NULL,'video_width','video_height');

	if (!addon_installed('unvalidated')) $validated=1;
	$map=array('title'=>insert_lang_comcode($title,2),'edit_date'=>$edit_date,'video_views'=>$views,'add_date'=>time(),'allow_rating'=>$allow_rating,'allow_comments'=>$allow_comments,'allow_trackbacks'=>$allow_trackbacks,'notes'=>$notes,'submitter'=>get_member(),'url'=>$url,'thumb_url'=>$thumb_url,'comments'=>insert_lang_comcode($comments,3),'cat'=>$cat,'validated'=>$validated,'video_length'=>$video_length,'video_width'=>$video_width,'video_height'=>$video_height);
	if (!is_null($id)) $map['id']=$id;
	$id=$GLOBALS['SITE_DB']->query_insert('videos',$map,true);

	log_it('ADD_VIDEO',strval($id),$title);

	if ($validated==1)
	{
		require_lang('galleries');
		require_code('notifications');
		$subject=do_lang('VIDEO_NOTIFICATION_MAIL_SUBJECT',get_site_name(),strip_comcode($title));
		$self_url=build_url(array('page'=>'galleries','type'=>'video','id'=>$id),get_module_zone('galleries'),NULL,false,false,true);
		$mail=do_lang('VIDEO_NOTIFICATION_MAIL',comcode_escape(get_site_name()),comcode_escape($title),array(comcode_escape($self_url->evaluate())));
		dispatch_notification('gallery_entry',$cat,$subject,$mail);
	}

	require_code('seo2');
	seo_meta_set_for_implicit('video',strval($id),array($comments),$comments);

	decache('side_root_galleries');
	decache('main_gallery_embed');

	return $id;
}

/**
 * Edit a video in a specified gallery.
 *
 * @param  AUTO_LINK		The ID of the entry to edit
 * @param  SHORT_TEXT	Video title
 * @param  ID_TEXT		The gallery name
 * @param  LONG_TEXT		The video comments
 * @param  URLPATH		The URL to the actual video
 * @param  URLPATH		The URL to the thumbnail of the actual video
 * @param  BINARY			Whether the video has been validated for display on the site
 * @param  BINARY			Whether the video may be rated
 * @param  BINARY			Whether the video may be commented upon
 * @param  BINARY			Whether the video may be trackbacked
 * @param  LONG_TEXT		Hidden notes associated with the video
 * @param  integer		The length of the video
 * @param  integer		The width of the video
 * @param  integer		The height of the video
 * @param  SHORT_TEXT	Meta keywords
 * @param  LONG_TEXT		Meta description
 */
function edit_video($id,$title,$cat,$comments,$url,$thumb_url,$validated,$allow_rating,$allow_comments,$allow_trackbacks,$notes,$video_length,$video_width,$video_height,$meta_keywords,$meta_description)
{
	require_code('urls2');
	suggest_new_idmoniker_for('galleries','video',strval($id),($title=='')?$comments:$title);

	$_title=$GLOBALS['SITE_DB']->query_value('videos','title',array('id'=>$id));
	$_comments=$GLOBALS['SITE_DB']->query_value('videos','comments',array('id'=>$id));

	require_code('files2');
	delete_upload('uploads/galleries','videos','url','id',$id,$url);
	delete_upload('uploads/galleries_thumbs','videos','thumb_url','id',$id,$thumb_url);

	require_code('transcoding');
	$url=transcode_video($url,'videos','url',NULL,'video_width','video_height');

	if (!addon_installed('unvalidated')) $validated=1;

	require_code('submit');
	$just_validated=(!content_validated('video',strval($id))) && ($validated==1);
	if ($just_validated)
	{
		send_content_validated_notification('video',strval($id));
	}

	$GLOBALS['SITE_DB']->query_update('videos',array('title'=>lang_remap_comcode($_title,$title),'edit_date'=>time(),'allow_rating'=>$allow_rating,'allow_comments'=>$allow_comments,'allow_trackbacks'=>$allow_trackbacks,'notes'=>$notes,'validated'=>$validated,'cat'=>$cat,'comments'=>lang_remap_comcode($_comments,$comments),'url'=>$url,'thumb_url'=>$thumb_url,'video_length'=>$video_length,'video_width'=>$video_width,'video_height'=>$video_height),array('id'=>$id),'',1);

	$self_url=build_url(array('page'=>'galleries','type'=>'video','id'=>$id),get_module_zone('galleries'),NULL,false,false,true);

	if ($just_validated)
	{
		require_lang('galleries');
		require_code('notifications');
		$subject=do_lang('VIDEO_NOTIFICATION_MAIL_SUBJECT',get_site_name(),strip_comcode($title));
		$mail=do_lang('VIDEO_NOTIFICATION_MAIL',comcode_escape(get_site_name()),comcode_escape($title),array(comcode_escape($self_url->evaluate())));
		dispatch_notification('gallery_entry',$cat,$subject,$mail);
	}

	log_it('EDIT_VIDEO',strval($id),$title);

	require_code('seo2');
	seo_meta_set_for_explicit('video',strval($id),$meta_keywords,$meta_description);

	decache('main_gallery_embed');

	require_lang('galleries');
	require_code('feedback');
	update_spacer_post($allow_comments!=0,'videos',strval($id),$self_url,do_lang('VIEW_VIDEO','','','',get_site_default_lang()),get_value('comment_forum__videos'));
}

/**
 * Delete a video in a specified gallery.
 *
 * @param  AUTO_LINK		The ID of the entry to delete
 * @param  boolean		Whether to delete the actual video file from disk as well as the entry
 */
function delete_video($id,$delete_full)
{
	$rows=$GLOBALS['SITE_DB']->query_select('videos',array('title','comments','cat'),array('id'=>$id));
	$title=$rows[0]['title'];
	$comments=$rows[0]['comments'];
	$cat=$rows[0]['cat'];

	log_it('DELETE_VIDEO',strval($id),get_translated_text($title));
	delete_lang($title);
	delete_lang($comments);

	if ($delete_full)
	{
		require_code('files2');
		delete_upload('uploads/galleries','videos','url','id',$id);
		delete_upload('uploads/galleries_thumbs','videos','thumb_url','id',$id);
	}

	// Delete from database
	$GLOBALS['SITE_DB']->query_delete('videos',array('id'=>$id),'',1);
	$GLOBALS['SITE_DB']->query_delete('rating',array('rating_for_type'=>'videos','rating_for_id'=>$id));
	$GLOBALS['SITE_DB']->query_delete('trackbacks',array('trackback_for_type'=>'videos','trackback_for_id'=>$id));

	require_code('seo2');
	seo_meta_erase_storage('video',strval($id));

	decache('side_root_galleries');
	decache('main_gallery_embed');
}

/**
 * Watermarks an image with the appropriate gallery watermarks.
 *
 * @param  ID_TEXT		The name of the gallery for the image
 * @param  PATH			The path to the image file
 * @param  string			The original file name of the image
 */
function watermark_gallery_image($gallery,$file_path,$filename)
{
	// We can't watermark an image we can't save
	require_code('images');
	if (!function_exists('imagecreatefromstring')) return;
	if (!is_saveable_image($filename)) return;

	// We need to find the most applicable gallery watermarks
	$watermark_top_left='';
	$watermark_top_right='';
	$watermark_bottom_left='';
	$watermark_bottom_right='';
	do
	{
		if ($gallery=='') return; // We couldn't find any matermarks

		$_gallery=$GLOBALS['SITE_DB']->query_select('galleries',array('parent_id','watermark_top_left','watermark_top_right','watermark_bottom_left','watermark_bottom_right'),array('name'=>$gallery),'',1);
		$watermark_top_left=$_gallery[0]['watermark_top_left'];
		$watermark_top_right=$_gallery[0]['watermark_top_right'];
		$watermark_bottom_left=$_gallery[0]['watermark_bottom_left'];
		$watermark_bottom_right=$_gallery[0]['watermark_bottom_right'];
		$gallery=$_gallery[0]['parent_id'];
	}
	while (($watermark_top_left=='') && ($watermark_top_right=='') && ($watermark_bottom_left=='') && ($watermark_bottom_right==''));

	// Now we must apply the watermarks
	$ext=get_file_extension($filename);
	$source=@imagecreatefromstring(file_get_contents($file_path));
	if ($source===false) return; // We couldn't load it for some reason

	// Apply the watermarks
	_watermark_corner($source,$watermark_top_left,0,0);
	_watermark_corner($source,$watermark_top_right,1,0);
	_watermark_corner($source,$watermark_bottom_left,0,1);
	_watermark_corner($source,$watermark_bottom_right,1,1);

	// Save
	if ($ext=='png') imagepng($source,$file_path);
	elseif (($ext=='jpg') || ($ext=='jpeg')) imagejpeg($source,$file_path);
	elseif ((function_exists('imagegif')) && ($ext=='gif')) imagegif($source,$file_path);

	// Clean up
	imagedestroy($source);
}

/**
 * Watermark the corner of an image.
 *
 * @param  resource		The image resource being watermarked
 * @param  URLPATH		The (local) URL to the watermark file
 * @param  BINARY			Whether a right hand side corner is being watermarked
 * @param  BINARY			Whether a bottom edge corner is being watermarked
 */
function _watermark_corner(/*&*/$source,$watermark_url,$x,$y)
{
	if ($watermark_url!='')
	{
		$watermark=@imagecreatefromstring(file_get_contents(rawurldecode($watermark_url)));
		if ($watermark!==false)
		{
			imagecolortransparent($watermark,imagecolorallocate($watermark,255,0,255));
			if ($x==1) $x=imagesx($source)-imagesx($watermark);
			if ($y==1) $y=imagesy($source)-imagesy($watermark);
			imagecopy($source,$watermark,$x,$y,0,0,imagesx($watermark),imagesy($watermark));
			imagedestroy($watermark);
		}
	}
}

/**
 * Make sure the detailed image file is not bigger than the defined box width.
 *
 * @param  PATH			The path to the image file
 * @param  string			The original filename of the image
 * @param  integer		The box width
 */
function constrain_gallery_image_to_max_size($file_path,$filename,$box_width)
{
	// We can't watermark an image we can't save
	require_code('images');
	if (!is_saveable_image($filename)) return;

	if ((get_option('is_on_gd')=='1') && (function_exists('imagecreatefromstring')))
		convert_image($file_path,$file_path,-1,-1,$box_width,false,get_file_extension($filename),true,true);
}

/**
 * Add a gallery with the specified parameters.
 *
 * @param  ID_TEXT		The gallery codename
 * @param  SHORT_TEXT	The full human-readeable name of the gallery
 * @param  LONG_TEXT		The description of the gallery
 * @param  SHORT_TEXT	Teaser text for the gallery
 * @param  LONG_TEXT		Hidden notes associated with the gallery
 * @param  ID_TEXT		The parent gallery (blank: no parent)
 * @param  BINARY			Whether images may be put in this gallery
 * @param  BINARY			Whether videos may be put in this gallery
 * @param  BINARY			Whether the gallery serves as a container for automatically created member galleries
 * @param  BINARY			Whether the gallery uses the flow mode interface
 * @param  URLPATH		The representative image of the gallery (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  BINARY			Whether rating are allowed
 * @param  BINARY			Whether comments are allowed
 * @param  boolean		Whether to skip the check for whether the gallery exists (useful for importers)
 * @param  ?TIME			The add time (NULL: now)
 * @param  ?MEMBER		The gallery owner (NULL: nobody)
 */
function add_gallery($name,$fullname,$description,$teaser,$notes,$parent_id,$accept_images=1,$accept_videos=1,$is_member_synched=0,$flow_mode_interface=0,$rep_image='',$watermark_top_left='',$watermark_top_right='',$watermark_bottom_left='',$watermark_bottom_right='',$allow_rating=1,$allow_comments=1,$skip_exists_check=false,$add_date=NULL,$g_owner=NULL)
{
	if (is_null($add_date)) $add_date=time();

	require_code('type_validation');
	if (!is_alphanumeric($name)) warn_exit(do_lang_tempcode('BAD_CODENAME'));

	if (!$skip_exists_check)
	{
		$test=$GLOBALS['SITE_DB']->query_value_null_ok('galleries','name',array('name'=>$name));
		if (!is_null($test)) warn_exit(do_lang_tempcode('ALREADY_EXISTS',escape_html($name)));
	}

	$GLOBALS['SITE_DB']->query_insert('galleries',
		array('name'=>$name,'add_date'=>$add_date,'description'=>insert_lang_comcode($description,2),'teaser'=>insert_lang_comcode($teaser,2),'notes'=>$notes,
				'fullname'=>insert_lang($fullname,1),
				'watermark_top_left'=>$watermark_top_left,'watermark_top_right'=>$watermark_top_right,
				'watermark_bottom_left'=>$watermark_bottom_left,'watermark_bottom_right'=>$watermark_bottom_right,
				'parent_id'=>$parent_id,'accept_images'=>$accept_images,'rep_image'=>$rep_image,
				'accept_videos'=>$accept_videos,'is_member_synched'=>$is_member_synched,'flow_mode_interface'=>$flow_mode_interface,
				'allow_rating'=>$allow_rating,'allow_comments'=>$allow_comments,'g_owner'=>$g_owner,
				'gallery_views'=>0,
				));

	log_it('ADD_GALLERY',$name,$fullname);

	require_code('seo2');
	seo_meta_set_for_implicit('gallery',$name,array($fullname,$description),$description);

	if (function_exists('decache'))
	{
		decache('main_top_galleries');
		decache('main_recent_galleries');
		decache('main_root_galleries');
		decache('side_root_galleries');
	}
}

/**
 * Edit a gallery.
 *
 * @param  ID_TEXT		The old gallery codename (in case we are renaming)
 * @param  ID_TEXT		The gallery codename (maybe the same as the old one)
 * @param  SHORT_TEXT	The full human-readeable name of the gallery
 * @param  LONG_TEXT		The description of the gallery
 * @param  SHORT_TEXT	Teaser text for the gallery
 * @param  LONG_TEXT		Hidden notes associated with the gallery
 * @param  ?ID_TEXT		The parent gallery (NULL: no parent)
 * @param  BINARY			Whether images may be put in this gallery
 * @param  BINARY			Whether videos may be put in this gallery
 * @param  BINARY			Whether the gallery serves as a container for automatically created member galleries
 * @param  BINARY			Whether the gallery uses the flow mode interface
 * @param  URLPATH		The representative image of the gallery (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  URLPATH		Watermark (blank: none)
 * @param  ?SHORT_TEXT	Meta keywords for this resource (NULL: do not edit)
 * @param  ?LONG_TEXT	Meta description for this resource (NULL: do not edit)
 * @param  BINARY			Whether rating are allowed
 * @param  BINARY			Whether comments are allowed
 * @param  ?MEMBER		The gallery owner (NULL: nobody)
 */
function edit_gallery($old_name,$name,$fullname,$description,$teaser,$notes,$parent_id=NULL,$accept_images=1,$accept_videos=1,$is_member_synched=0,$flow_mode_interface=0,$rep_image='',$watermark_top_left='',$watermark_top_right='',$watermark_bottom_left='',$watermark_bottom_right='',$meta_keywords=NULL,$meta_description=NULL,$allow_rating=1,$allow_comments=1,$g_owner=NULL)
{
	require_code('urls2');
	suggest_new_idmoniker_for('galleries','misc',$name,$fullname);

	$under_category_id=$parent_id;
	while (($under_category_id!='') && ($under_category_id!=STRING_MAGIC_NULL))
	{
		if ($name==$under_category_id) warn_exit(do_lang_tempcode('OWN_PARENT_ERROR'));
		$under_category_id=$GLOBALS['SITE_DB']->query_value('galleries','parent_id',array('name'=>$under_category_id));
	}

	if (is_null($parent_id)) $parent_id='';

	require_code('seo2');

	if ($old_name!=$name)
	{
		require_code('type_validation');
		if (!is_alphanumeric($name)) warn_exit(do_lang_tempcode('BAD_CODENAME'));

		$test=$GLOBALS['SITE_DB']->query_value_null_ok('galleries','name',array('name'=>$name));
		if (!is_null($test)) warn_exit(do_lang_tempcode('ALREADY_EXISTS',escape_html($name)));

		seo_meta_erase_storage('gallery',$old_name);
		$GLOBALS['SITE_DB']->query_update('images',array('cat'=>$name),array('cat'=>$old_name));
		$GLOBALS['SITE_DB']->query_update('videos',array('cat'=>$name),array('cat'=>$old_name));
		$GLOBALS['SITE_DB']->query_update('galleries',array('parent_id'=>$name),array('parent_id'=>$old_name));
		$types=$GLOBALS['SITE_DB']->query_select('award_types',array('id'),array('a_content_type'=>'gallery'));
		foreach ($types as $type)
		{
			$GLOBALS['SITE_DB']->query_update('award_archive',array('content_id'=>$name),array('content_id'=>$old_name,'a_type_id'=>$type['id']));
		}
	}

	if (!is_null($meta_keywords)) seo_meta_set_for_explicit('gallery',$name,$meta_keywords,$meta_description);

	$myrows=$GLOBALS['SITE_DB']->query_select('galleries',array('fullname','description','teaser'),array('name'=>$old_name),'',1);
	if (!array_key_exists(0,$myrows)) warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
	$myrow=$myrows[0];

	$map=array('name'=>$name,'notes'=>$notes,'fullname'=>lang_remap($myrow['fullname'],$fullname),
					'description'=>lang_remap_comcode($myrow['description'],$description),'teaser'=>lang_remap_comcode($myrow['teaser'],$teaser),'parent_id'=>$parent_id,'accept_images'=>$accept_images,
					'accept_videos'=>$accept_videos,'is_member_synched'=>$is_member_synched,'flow_mode_interface'=>$flow_mode_interface,
					'allow_rating'=>$allow_rating,'allow_comments'=>$allow_comments,'g_owner'=>$g_owner);

	require_code('files2');

	if (!is_null($rep_image))
	{
		$map['rep_image']=$rep_image;
		delete_upload('uploads/grepimages','galleries','rep_image','name',$old_name,$rep_image);
	}
	if (!is_null($watermark_top_left))
	{
		$map['watermark_top_left']=$watermark_top_left;
		delete_upload('uploads/watermarks','galleries','watermark_top_left','name',$old_name,$watermark_top_left);
	}
	if (!is_null($watermark_top_right))
	{
		$map['watermark_top_right']=$watermark_top_right;
		delete_upload('uploads/watermarks','galleries','watermark_top_right','name',$old_name,$watermark_top_right);
	}
	if (!is_null($watermark_bottom_left))
	{
		$map['watermark_bottom_left']=$watermark_bottom_left;
		delete_upload('uploads/watermarks','galleries','watermark_bottom_left','name',$old_name,$watermark_bottom_left);
	}
	if (!is_null($watermark_bottom_right))
	{
		$map['watermark_bottom_right']=$watermark_bottom_right;
		delete_upload('uploads/watermarks','galleries','watermark_bottom_right','name',$old_name,$watermark_bottom_right);
	}

	$GLOBALS['SITE_DB']->query_update('galleries',$map,array('name'=>$old_name),'',1);

	log_it('EDIT_GALLERY',$name,$fullname);

	$GLOBALS['SITE_DB']->query_update('group_category_access',array('category_name'=>$name),array('module_the_name'=>'galleries','category_name'=>$old_name));

	decache('main_top_galleries');
	decache('main_recent_galleries');
	decache('main_root_galleries');
	decache('side_root_galleries');

	require_code('feedback');
	update_spacer_post($allow_comments!=0,'galleries',$name,build_url(array('page'=>'galleries','type'=>'misc','id'=>$name),get_module_zone('galleries'),NULL,false,false,true),$fullname,get_value('comment_forum__galleries'));
}

/**
 * Delete a specified gallery.
 *
 * @param  ID_TEXT		The gallery codename
 */
function delete_gallery($name)
{
	if ($name=='') warn_exit(do_lang_tempcode('NO_DELETE_ROOT'));

	$rows=$GLOBALS['SITE_DB']->query_select('galleries',array('*'),array('name'=>$name),'',1);
	if (!array_key_exists(0,$rows)) warn_exit(do_lang_tempcode('MISSING_RESOURCE'));

	require_code('files2');
	delete_upload('uploads/grepimages','galleries','rep_image','name',$name);
	delete_upload('uploads/watermarks','galleries','watermark_top_left','name',$name);
	delete_upload('uploads/watermarks','galleries','watermark_top_right','name',$name);
	delete_upload('uploads/watermarks','galleries','watermark_bottom_left','name',$name);
	delete_upload('uploads/watermarks','galleries','watermark_bottom_right','name',$name);

	log_it('DELETE_GALLERY',$name,get_translated_text($rows[0]['fullname']));

	delete_lang($rows[0]['fullname']);
	delete_lang($rows[0]['description']);
	delete_lang($rows[0]['teaser']);

	// Images and videos are deleted, because we are deleting the _gallery_, not just a category (nobody is going to be deleting galleries with the expectation of moving the image to a different one in bulk - unlike download categories, for example).
	if (function_exists('set_time_limit')) @set_time_limit(0);
	do
	{
		$images=$GLOBALS['SITE_DB']->query_select('images',array('id'),array('cat'=>$name),'',200);
		foreach ($images as $image)
		{
			delete_image($image['id'],false);
		}
	}
	while ($images!=array());
	do
	{
		$videos=$GLOBALS['SITE_DB']->query_select('videos',array('id'),array('cat'=>$name),'',200);
		foreach ($videos as $video)
		{
			delete_video($video['id'],false);
		}
	}
	while ($images!=array());
	//... but the subgalleries remain
	$GLOBALS['SITE_DB']->query_update('galleries',array('parent_id'=>$rows[0]['parent_id']),array('parent_id'=>$name));

	$GLOBALS['SITE_DB']->query_delete('galleries',array('name'=>$name),'',1);

	$GLOBALS['SITE_DB']->query_delete('rating',array('rating_for_type'=>'images','rating_for_id'=>$name));
	$GLOBALS['SITE_DB']->query_delete('rating',array('rating_for_type'=>'videos','rating_for_id'=>$name));

	require_code('seo2');
	seo_meta_erase_storage('gallery',$name);

	$GLOBALS['SITE_DB']->query_delete('group_category_access',array('module_the_name'=>'galleries','category_name'=>$name));
	$GLOBALS['SITE_DB']->query_delete('gsp',array('module_the_name'=>'galleries','category_name'=>$name));

	decache('main_top_galleries');
	decache('main_recent_galleries');
	decache('main_root_galleries');
	decache('side_root_galleries');
}

/**
 * The UI shows member galleries that do not exist. If it is a member gallery, and it does not exist, it'll need making, before something can be added. This gallery performs the check and makes the gallery if needed.
 *
 * @param  ID_TEXT		The gallery name
 */
function make_member_gallery_if_needed($cat)
{
	// If it is a non-member gallery, it must surely exist, as we have no interface to choose non-existant ones (it's safe enough to assume it hasn't been deleted suddenly)

	if (substr($cat,0,7)!='member_') return;

	// Test to see if it exists
	$test=$GLOBALS['SITE_DB']->query_value_null_ok('galleries','name',array('name'=>$cat));
	if (is_null($test))
	{
		$parts=explode('_',$cat,3);
		$member=intval($parts[1]); // Almost certainly going to be same as get_member(), but we might as well be general here

		if (!has_specific_permission($member,'have_personal_category','cms_galleries')) return;

		// Find about parent (new gallery inherits)
		$parent_id=$parts[2];
		$_parent_info=$GLOBALS['SITE_DB']->query_select('galleries',array('accept_images','accept_videos','flow_mode_interface','fullname'),array('name'=>$parent_id),'',1);
		if (!array_key_exists(0,$_parent_info)) fatal_exit(do_lang_tempcode('INTERNAL_ERROR'));
		$parent_info=$_parent_info[0];

		$username=$GLOBALS['FORUM_DRIVER']->get_username($member);
		if (is_null($username)) warn_exit(do_lang_tempcode('_USER_NO_EXIST',escape_html($username)));
		add_gallery($cat,do_lang('PERSONAL_GALLERY_OF',$username,get_translated_text($parent_info['fullname'])),'','','',$parent_id,$parent_info['accept_images'],$parent_info['accept_videos'],0,$parent_info['flow_mode_interface']);

		$rows=$GLOBALS['SITE_DB']->query_select('group_category_access',array('group_id'),array('module_the_name'=>'galleries','category_name'=>$parent_id));
		foreach ($rows as $row)
		{
			$GLOBALS['SITE_DB']->query_insert('group_category_access',array('module_the_name'=>'galleries','category_name'=>$cat,'group_id'=>$row['group_id']));
		}
	}
}


