<?php
	$sendy_file_manager_cwd = getcwd();
	chdir(__DIR__.'/..');
	include('functions.php');
	include('login/auth.php');
	chdir($sendy_file_manager_cwd);

	header('X-Content-Type-Options: nosniff');
	header('X-Frame-Options: SAMEORIGIN');
	header("Content-Security-Policy: default-src 'self'; img-src 'self' data: https: http:; style-src 'unsafe-inline'; script-src 'unsafe-inline'; frame-ancestors 'self'; base-uri 'none'; form-action 'self'");
	header('Referrer-Policy: same-origin');
	header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
	header('Pragma: no-cache');

	function sendy_fm_escape($value)
	{
		return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
	}

	function sendy_fm_hash_equals($known_string, $user_string)
	{
		if(function_exists('hash_equals')) return hash_equals($known_string, $user_string);
		if(!is_string($known_string) || !is_string($user_string)) return false;
		$known_length = strlen($known_string);
		if($known_length!==strlen($user_string)) return false;
		$result = 0;
		for($i=0; $i<$known_length; $i++) $result |= ord($known_string[$i]) ^ ord($user_string[$i]);
		return $result===0;
	}

	function sendy_fm_normalize_path($path)
	{
		$path = str_replace('\\', '/', (string)$path);
		$path = trim($path, '/');
		if($path==='') return '';

		$parts = explode('/', $path);
		$clean = array();
		foreach($parts as $part)
		{
			if($part==='' || $part==='.' || $part==='..' || strpos($part, "\0")!==false) return false;
			$clean[] = $part;
		}
		return implode('/', $clean);
	}

	function sendy_fm_is_within_root($root, $path)
	{
		$root = rtrim(str_replace('\\', '/', $root), '/');
		$path = str_replace('\\', '/', $path);
		return $path===$root || strpos($path, $root.'/')===0;
	}

	function sendy_fm_existing_path($root, $relative)
	{
		$relative = sendy_fm_normalize_path($relative);
		if($relative===false) return false;
		$path = $relative==='' ? $root : $root.'/'.$relative;
		$real = realpath($path);
		if($real===false || !sendy_fm_is_within_root($root, $real)) return false;
		return $real;
	}

	function sendy_fm_safe_name($name)
	{
		$name = basename(str_replace('\\', '/', trim((string)$name)));
		if($name==='' || substr($name, 0, 1)==='.') return false;
		$name = preg_replace('/[\x00-\x1F\x7F]/u', '', $name);
		$name = preg_replace('/[^\pL\pN._ -]+/u', '-', $name);
		$name = trim($name, " .\t\n\r\0\x0B");
		if($name==='' || $name==='.' || $name==='..' || substr($name, 0, 1)==='.') return false;
		return $name;
	}

	function sendy_fm_is_protected_name($name)
	{
		$name = strtolower((string)$name);
		return in_array($name, array('.htaccess', '.user.ini', 'web.config'), true);
	}

	function sendy_fm_is_dangerous_extension($name)
	{
		return preg_match('/\.(?:php(?:[3-8])?|phtml|pht|phar|phps|cgi|pl|py|rb|sh|shtml)(?:\.|$)/i', (string)$name)===1;
	}

	function sendy_fm_allowed_image_types()
	{
		return array(
			'jpg' => array('image/jpeg'),
			'jpeg' => array('image/jpeg'),
			'png' => array('image/png'),
			'gif' => array('image/gif'),
			'webp' => array('image/webp')
		);
	}

	function sendy_fm_is_image_name($name)
	{
		$types = sendy_fm_allowed_image_types();
		return isset($types[strtolower(pathinfo((string)$name, PATHINFO_EXTENSION))]);
	}

	function sendy_fm_upload_error($temporary_file, $filename, $error, $allowed_extensions)
	{
		if((int)$error!==UPLOAD_ERR_OK)
		{
			if((int)$error===UPLOAD_ERR_INI_SIZE || (int)$error===UPLOAD_ERR_FORM_SIZE) return _('The image exceeds the server upload limit.');
			if((int)$error===UPLOAD_ERR_NO_FILE) return _('No file data was received.');
			return _('The server could not receive the file.');
		}
		if($filename===false) return _('The file has an invalid filename.');
		if(sendy_fm_is_protected_name($filename) || sendy_fm_is_dangerous_extension($filename)) return _('That file type is not allowed for security reasons.');
		if(!is_uploaded_file($temporary_file)) return _('The uploaded file could not be verified.');
		if(filesize($temporary_file) > 25 * 1024 * 1024) return _('Files must be 25 MB or smaller.');
		$types = sendy_fm_allowed_image_types();
		$extension = strtolower(pathinfo((string)$filename, PATHINFO_EXTENSION));
		if($extension==='' || !in_array($extension, $allowed_extensions, true)) return sprintf(_('.%s files are not allowed by this brand\'s attachment settings.'), $extension==='' ? '?' : $extension);
		if(isset($types[$extension]))
		{
			$image = @getimagesize($temporary_file);
			if($image===false || !isset($image['mime']) || !in_array(strtolower($image['mime']), $types[$extension], true)) return _('The file contents do not match the selected image format.');
		}
		return null;
	}

	function sendy_fm_unique_target($directory, $filename)
	{
		$extension = pathinfo($filename, PATHINFO_EXTENSION);
		$stem = pathinfo($filename, PATHINFO_FILENAME);
		$target = $directory.'/'.$filename;
		$counter = 1;
		while(file_exists($target))
		{
			$suffix = '-'.$counter;
			$target = $directory.'/'.$stem.$suffix.($extension==='' ? '' : '.'.$extension);
			$counter++;
		}
		return $target;
	}

	function sendy_fm_remove($root, $path)
	{
		if(!sendy_fm_is_within_root($root, $path) || $path===$root) return false;
		if(is_link($path) || is_file($path)) return @unlink($path);
		if(!is_dir($path)) return false;

		$items = @scandir($path);
		if($items===false) return false;
		foreach($items as $item)
		{
			if($item==='.' || $item==='..') continue;
			if(!sendy_fm_remove($root, $path.'/'.$item)) return false;
		}
		return @rmdir($path);
	}

	function sendy_fm_public_url($app_id, $relative)
	{
		$segments = $relative==='' ? array() : explode('/', str_replace('\\', '/', $relative));
		$segments = array_map('rawurlencode', $segments);
		return rtrim(get_app_info('path'), '/').'/uploads/'.(int)$app_id.($segments ? '/'.implode('/', $segments) : '');
	}

	function sendy_fm_format_size($bytes)
	{
		$bytes = (int)$bytes;
		if($bytes < 1024) return $bytes.' B';
		if($bytes < 1048576) return round($bytes / 1024, 1).' KB';
		if($bytes < 1073741824) return round($bytes / 1048576, 1).' MB';
		return round($bytes / 1073741824, 1).' GB';
	}

	function sendy_fm_redirect($app_id, $relative, $page=1)
	{
		$url = 'index.php?i='.(int)$app_id.'&p='.rawurlencode($relative).'&page='.max(1, (int)$page);
		if(isset($GLOBALS['sendy_fm_pending_message']) && is_array($GLOBALS['sendy_fm_pending_message']))
		{
			$url .= '&fm_message='.rawurlencode($GLOBALS['sendy_fm_pending_message']['text']);
			$url .= '&fm_type='.rawurlencode($GLOBALS['sendy_fm_pending_message']['type']);
		}
		header('Location: '.$url);
		exit;
	}

	function sendy_fm_set_message($message, $type='success')
	{
		$type = in_array($type, array('success', 'warning', 'error'), true) ? $type : 'error';
		$GLOBALS['sendy_fm_pending_message'] = array('text' => (string)$message, 'type' => $type);
	}

	function sendy_fm_csrf_token($app_id)
	{
		$secret = session_id().'|'.(string)get_app_info('userID').'|'.(string)get_app_info('auth_salt').'|'.(string)get_app_info('password');
		return hash_hmac('sha256', 'sendy-file-manager|'.(int)$app_id, $secret);
	}

	function sendy_fm_upload_response($message, $type, $uploaded, $relative, $app_id, $page=1)
	{
		if(isset($_POST['sendy_file_manager_response']) && $_POST['sendy_file_manager_response']==='json')
		{
			header('Content-Type: application/json; charset=utf-8');
			echo json_encode(array(
				'ok' => $uploaded > 0,
				'uploaded' => (int)$uploaded,
				'message' => $message,
				'refresh' => 'index.php?i='.(int)$app_id.'&p='.rawurlencode($relative).'&page='.max(1, (int)$page)
			));
			exit;
		}
		sendy_fm_set_message($message, $type);
		sendy_fm_redirect($app_id, $relative, $page);
	}

	$app_id = isset($_REQUEST['i']) && is_numeric($_REQUEST['i']) ? (int)$_REQUEST['i'] : 0;
	if($app_id<=0 || $app_id!==(int)get_app_info('app')) exit;
	if(get_app_info('is_sub_user') && $app_id!==(int)get_app_info('restricted_to_app')) exit;

	$q = 'SELECT id, app_name, allowed_attachments FROM apps WHERE id = '.$app_id.' AND userID = '.get_app_info('main_userID').' LIMIT 1';
	$r = mysqli_query($mysqli, $q);
	if(!$r || mysqli_num_rows($r)===0) exit;
	$app_row = mysqli_fetch_array($r);
	$app_name = $app_row['app_name'];
	$brand_allowed_extensions = array();
	foreach(explode(',', strtolower((string)$app_row['allowed_attachments'])) as $extension)
	{
		$extension = ltrim(trim($extension), '.');
		if($extension!=='' && preg_match('/^[a-z0-9]+$/', $extension)) $brand_allowed_extensions[] = $extension;
	}
	$brand_allowed_extensions = array_values(array_unique($brand_allowed_extensions));
	$allowed_upload_extensions = array();
	foreach($brand_allowed_extensions as $extension)
	{
		if(!sendy_fm_is_dangerous_extension('file.'.$extension)) $allowed_upload_extensions[] = $extension;
	}
	$uploads_enabled = count($allowed_upload_extensions)>0;

	$uploads_root = realpath(__DIR__.'/../../uploads');
	if($uploads_root===false)
	{
		@mkdir(__DIR__.'/../../uploads', 0755, true);
		$uploads_root = realpath(__DIR__.'/../../uploads');
	}
	if($uploads_root===false) exit;

	$root = sendy_uploads_path($app_id, '', $uploads_root, true);
	$root = realpath($root);
	if($root===false || !sendy_fm_is_within_root($uploads_root, $root)) exit;

	$relative = isset($_REQUEST['p']) ? sendy_fm_normalize_path($_REQUEST['p']) : '';
	$page = isset($_REQUEST['page']) && is_numeric($_REQUEST['page']) ? max(1, (int)$_REQUEST['page']) : 1;
	if($relative===false) $relative = '';
	$current = sendy_fm_existing_path($root, $relative);
	if($current===false || !is_dir($current))
	{
		$relative = '';
		$current = $root;
	}

	$csrf_token = sendy_fm_csrf_token($app_id);

	if(isset($_GET['download']))
	{
		$download_relative = sendy_fm_normalize_path($_GET['download']);
		$download = $download_relative===false ? false : sendy_fm_existing_path($root, $download_relative);
		if($download===false || !is_file($download) || is_link($download))
		{
			http_response_code(404);
			exit;
		}
		$download_name = preg_replace('/[\r\n"]/', '', basename($download));
		header('Content-Type: application/octet-stream');
		header('Content-Disposition: attachment; filename="'.$download_name.'"');
		header('Content-Length: '.filesize($download));
		header('Cache-Control: private, no-store');
		readfile($download);
		exit;
	}

	if($_SERVER['REQUEST_METHOD']==='POST')
	{
		if(!isset($_POST['token']) || !sendy_fm_hash_equals($csrf_token, (string)$_POST['token']))
		{
			http_response_code(403);
			sendy_fm_set_message(_('Your session has expired. Please try again.'), 'error');
			sendy_fm_redirect($app_id, $relative, $page);
		}

		$action = isset($_POST['action']) ? $_POST['action'] : '';
		if($action==='upload')
		{
			$uploaded = 0;
			$rejected = 0;
			$rejection_messages = array();
			if(!is_writable($current)) @chmod($current, 0777);
			$names = isset($_FILES['images']['name']) ? $_FILES['images']['name'] : array();
			$temporary_files = isset($_FILES['images']['tmp_name']) ? $_FILES['images']['tmp_name'] : array();
			$errors = isset($_FILES['images']['error']) ? $_FILES['images']['error'] : array();
			if(!is_array($names))
			{
				$names = array($names);
				$temporary_files = array($temporary_files);
				$errors = array($errors);
			}

			for($i=0; $i<count($names); $i++)
			{
				$name = sendy_fm_safe_name($names[$i]);
				$tmp = isset($temporary_files[$i]) ? $temporary_files[$i] : '';
				$error = isset($errors[$i]) ? (int)$errors[$i] : UPLOAD_ERR_NO_FILE;
				$validation_error = sendy_fm_upload_error($tmp, $name, $error, $allowed_upload_extensions);
				if($validation_error!==null)
				{
					$rejected++;
					$rejection_messages[] = $validation_error;
					continue;
				}
				if(!is_writable($current))
				{
					$rejected++;
					$rejection_messages[] = _('The upload folder is not writable by the web server.');
					continue;
				}
				$target = sendy_fm_unique_target($current, $name);
				if(move_uploaded_file($tmp, $target))
				{
					@chmod($target, 0644);
					$uploaded++;
				}
				else
				{
					$rejected++;
					$rejection_messages[] = _('The server could not save the uploaded image.');
				}
			}

			if($uploaded>0 && $rejected===0)
				sendy_fm_upload_response(sprintf(_('%s file(s) uploaded.'), $uploaded), 'success', $uploaded, $relative, $app_id, 1);
			else if($uploaded>0)
				sendy_fm_upload_response(sprintf(_('%s file(s) uploaded; %s file(s) rejected. %s'), $uploaded, $rejected, isset($rejection_messages[0]) ? $rejection_messages[0] : ''), 'warning', $uploaded, $relative, $app_id, 1);
			else
				sendy_fm_upload_response(isset($rejection_messages[0]) ? $rejection_messages[0] : _('No files were uploaded.'), 'error', 0, $relative, $app_id, 1);
		}
		else if($action==='new_folder')
		{
			$name = isset($_POST['name']) ? sendy_fm_safe_name($_POST['name']) : false;
			if($name===false || sendy_fm_is_protected_name($name) || file_exists($current.'/'.$name))
				sendy_fm_set_message(_('Unable to create that folder.'), 'error');
			else if(@mkdir($current.'/'.$name, 0755))
				sendy_fm_set_message(_('Folder created.'));
			else
				sendy_fm_set_message(_('Unable to create that folder.'), 'error');
			sendy_fm_redirect($app_id, $relative, $page);
		}
		else if($action==='rename')
		{
			$source_relative = isset($_POST['item']) ? sendy_fm_normalize_path($_POST['item']) : false;
			$source = $source_relative===false ? false : sendy_fm_existing_path($root, $source_relative);
			$new_name = isset($_POST['name']) ? sendy_fm_safe_name($_POST['name']) : false;
			$renamed = false;
			$rename_error = _('Unable to rename that item.');
			if($source!==false && $source!==$root && $new_name!==false)
			{
				$parent = dirname($source);
				$old_extension = strtolower(pathinfo(basename($source), PATHINFO_EXTENSION));
				$new_extension = strtolower(pathinfo($new_name, PATHINFO_EXTENSION));
				if(is_file($source) && $old_extension!=='' && $new_extension==='')
				{
					$new_name .= '.'.$old_extension;
					$new_extension = $old_extension;
				}
				$target = $parent.'/'.$new_name;
				if(sendy_fm_is_protected_name($new_name) || sendy_fm_is_dangerous_extension($new_name))
					$rename_error = _('That filename is not allowed.');
				else if(is_file($source) && $old_extension!==$new_extension)
					$rename_error = sprintf(_('Please keep the .%s file extension.'), $old_extension);
				else if(file_exists($target))
					$rename_error = _('A file or folder with that name already exists.');
				else if(sendy_fm_is_within_root($root, $target))
				{
					$renamed = @rename($source, $target);
					if(!$renamed) $rename_error = _('The web server could not rename that item.');
				}
			}
			sendy_fm_set_message($renamed ? _('Item renamed.') : $rename_error, $renamed ? 'success' : 'error');
			sendy_fm_redirect($app_id, $relative, $page);
		}
		else if($action==='delete')
		{
			$items = isset($_POST['items']) ? $_POST['items'] : array();
			if(!is_array($items)) $items = array($items);
			$deleted = 0;
			$failed = 0;
			foreach(array_unique($items) as $item)
			{
				$item_relative = sendy_fm_normalize_path($item);
				$item_path = $item_relative===false ? false : sendy_fm_existing_path($root, $item_relative);
				if($item_path!==false && $item_path!==$root && !sendy_fm_is_protected_name(basename($item_path)) && sendy_fm_remove($root, $item_path)) $deleted++;
				else $failed++;
			}
			if($deleted>0 && $failed===0) sendy_fm_set_message(sprintf(_('%s item(s) deleted.'), $deleted));
			else if($deleted>0) sendy_fm_set_message(sprintf(_('%s item(s) deleted; %s failed.'), $deleted, $failed), 'warning');
			else sendy_fm_set_message(_('No items were deleted.'), 'error');
			sendy_fm_redirect($app_id, $relative, $page);
		}
	}

	$message = null;
	if(isset($_GET['fm_message']) && $_GET['fm_message']!=='')
	{
		$message_type = isset($_GET['fm_type']) && in_array($_GET['fm_type'], array('success', 'warning', 'error'), true) ? $_GET['fm_type'] : 'error';
		$message = array('text' => (string)$_GET['fm_message'], 'type' => $message_type);
	}

	$entries = array();
	$scan = scandir($current);
	if($scan!==false)
	{
		foreach($scan as $name)
		{
			if($name==='.' || $name==='..' || substr($name, 0, 1)==='.' || sendy_fm_is_protected_name($name)) continue;
			$path = $current.'/'.$name;
			if(is_link($path)) continue;
			$item_relative = $relative==='' ? $name : $relative.'/'.$name;
			$entries[] = array(
				'name' => $name,
				'relative' => $item_relative,
				'is_dir' => is_dir($path),
				'is_image' => is_file($path) && sendy_fm_is_image_name($name),
				'dangerous' => is_file($path) && sendy_fm_is_dangerous_extension($name),
				'size' => is_file($path) ? filesize($path) : null,
				'modified' => filemtime($path)
			);
		}
	}
	usort($entries, function($a, $b) {
		if($a['modified']!==$b['modified']) return $a['modified']>$b['modified'] ? -1 : 1;
		return strcasecmp($a['name'], $b['name']);
	});
	$items_per_page = 15;
	$total_items = count($entries);
	$total_pages = max(1, (int)ceil($total_items / $items_per_page));
	if($page>$total_pages) $page = $total_pages;
	$page_offset = ($page-1) * $items_per_page;
	$entries = array_slice($entries, $page_offset, $items_per_page);
	$page_first_item = $total_items ? $page_offset+1 : 0;
	$page_last_item = min($page_offset+$items_per_page, $total_items);
	$pagination_url = '?i='.$app_id.'&p='.rawurlencode($relative).'&page=';

	$parent = '';
	if($relative!=='')
	{
		$parts = explode('/', $relative);
		array_pop($parts);
		$parent = implode('/', $parts);
	}
	$dark_mode = get_app_info('dark_mode');
?>
<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title><?php echo sendy_fm_escape(_('File manager'));?></title>
	<style>
		:root { color-scheme: <?php echo $dark_mode ? 'dark' : 'light';?>; --bg:<?php echo $dark_mode ? '#17191d' : '#f6f8fb';?>; --panel:<?php echo $dark_mode ? '#22252b' : '#fff';?>; --text:<?php echo $dark_mode ? '#e7e9ed' : '#283142';?>; --muted:<?php echo $dark_mode ? '#a6abb5' : '#6b7280';?>; --line:<?php echo $dark_mode ? '#383d47' : '#e3e8ef';?>; --accent:#5b55d6; --danger:#c94040; --success:#2e8b57; }
		* { box-sizing:border-box; }
		body { margin:0; background:var(--bg); color:var(--text); font:14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; }
		button,input { font:inherit; }
		.wrap { max-width:1180px; margin:0 auto; padding:24px; }
		.header { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:18px; }
		h1 { margin:0; font-size:24px; }
		.subtle { color:var(--muted); }
		.panel { background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:0 8px 24px rgba(0,0,0,.06); }
		.upload { display:block; margin-bottom:18px; padding:34px 20px; border:2px dashed #aaa6ed; border-radius:12px; background:rgba(91,85,214,.06); text-align:center; cursor:pointer; transition:.15s ease; }
		.upload.drag { border-color:var(--accent); background:rgba(91,85,214,.14); transform:translateY(-1px); }
		.upload.disabled { cursor:not-allowed; border-color:var(--line); background:var(--panel); opacity:.72; }
		.upload strong { display:block; font-size:20px; margin-bottom:6px; }
		.upload input { position:absolute; width:1px; height:1px; opacity:0; }
		.toolbar { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:10px; padding:14px 16px; border-bottom:1px solid var(--line); }
		.toolbar-group { display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
		.btn { border:1px solid var(--line); border-radius:7px; padding:7px 11px; background:var(--panel); color:var(--text); cursor:pointer; text-decoration:none; }
		.btn:hover { border-color:#aaa6ed; }
		.btn.primary { color:#fff; border-color:var(--accent); background:var(--accent); }
		.btn.danger { color:#fff; border-color:var(--danger); background:var(--danger); }
		.btn.small { padding:5px 8px; font-size:12px; }
		.breadcrumbs { display:flex; flex-wrap:wrap; gap:5px; align-items:center; }
		.breadcrumbs a { color:var(--accent); text-decoration:none; }
		.message { margin-bottom:16px; padding:11px 14px; border-radius:8px; border:1px solid var(--line); background:var(--panel); }
		.message.success { border-color:#78bb96; }
		.message.warning { border-color:#d5a63e; }
		.message.error { border-color:#d26767; }
		.loading-overlay { display:none; position:fixed; z-index:2000; inset:0; align-items:center; justify-content:center; background:rgba(20,24,33,.48); backdrop-filter:blur(2px); }
		.loading-overlay.active { display:flex; }
		.loading-card { min-width:220px; padding:24px 28px; border:1px solid var(--line); border-radius:12px; background:var(--panel); box-shadow:0 18px 50px rgba(0,0,0,.3); text-align:center; }
		.loading-spinner { width:34px; height:34px; margin:0 auto 13px; border:4px solid var(--line); border-top-color:var(--accent); border-radius:50%; animation:sendy-fm-spin .75s linear infinite; }
		.loading-text { font-weight:600; }
		@keyframes sendy-fm-spin { to { transform:rotate(360deg); } }
		@media(prefers-reduced-motion:reduce) { .loading-spinner { animation-duration:1.5s; } }
		.table-wrap { overflow-x:auto; }
		.pagination { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px; padding:13px 16px; border-top:1px solid var(--line); }
		.pagination-info { color:var(--muted); }
		.pagination-links { display:flex; align-items:center; gap:5px; }
		.pagination-links .current { border-color:var(--accent); background:var(--accent); color:#fff; }
		.pagination-links .disabled { opacity:.45; pointer-events:none; }
		table { width:100%; border-collapse:collapse; }
		th,td { padding:12px 14px; border-bottom:1px solid var(--line); text-align:left; vertical-align:middle; }
		th { font-size:12px; color:var(--muted); text-transform:uppercase; letter-spacing:.04em; }
		tr:last-child td { border-bottom:0; }
		.name-cell { position:relative; min-width:260px; }
		.name-link { color:var(--text); text-decoration:none; font-weight:600; overflow-wrap:anywhere; }
		.name-link:hover { color:var(--accent); }
		.icon { display:inline-flex; width:28px; height:28px; margin-right:8px; align-items:center; justify-content:center; border-radius:7px; background:rgba(91,85,214,.11); }
		.actions { display:flex; flex-wrap:wrap; gap:6px; min-width:250px; }
		.preview { display:none; position:fixed; z-index:1000; max-width:320px; max-height:220px; padding:5px; background:var(--panel); border:1px solid var(--line); border-radius:9px; box-shadow:0 14px 36px rgba(0,0,0,.28); pointer-events:none; }
		.name-cell:hover .preview { display:block; }
		.empty { padding:44px 20px; text-align:center; color:var(--muted); }
		.danger-text { color:var(--danger); font-weight:600; }
		#selected-count { min-width:78px; color:var(--muted); }
		@media(max-width:720px) { .wrap{padding:14px}.header{align-items:flex-start;flex-direction:column}.upload{padding:25px 12px}th:nth-child(3),td:nth-child(3),th:nth-child(4),td:nth-child(4){display:none}.actions{min-width:210px} }
	</style>
</head>
<body>
	<div class="wrap">
		<div class="header">
			<div><h1><?php echo sendy_fm_escape(_('File manager'));?></h1><div class="subtle"><?php echo sendy_fm_escape($app_name);?></div></div>
			<button type="button" class="btn" id="new-folder-btn">+ <?php echo sendy_fm_escape(_('New folder'));?></button>
		</div>

		<?php if($message):?><div class="message <?php echo sendy_fm_escape($message['type']);?>"><?php echo sendy_fm_escape($message['text']);?></div><?php endif;?>

		<form id="upload-form" method="post" enctype="multipart/form-data">
			<input type="hidden" name="i" value="<?php echo $app_id;?>">
			<input type="hidden" name="p" value="<?php echo sendy_fm_escape($relative);?>">
			<input type="hidden" name="page" value="<?php echo $page;?>">
			<input type="hidden" name="token" value="<?php echo sendy_fm_escape($csrf_token);?>">
			<input type="hidden" name="action" value="upload">
			<label class="upload<?php echo $uploads_enabled ? '' : ' disabled';?>" id="drop-zone">
				<strong id="upload-title"><?php echo sendy_fm_escape($uploads_enabled ? _('Drop files here to upload') : _('File uploads are disabled for this brand'));?></strong>
				<span id="upload-help"><?php echo sendy_fm_escape($uploads_enabled ? sprintf(_('or click to choose %s files up to 25 MB'), strtoupper(implode(', ', $allowed_upload_extensions))) : _('Add extensions under “Allowed attachments file types” in Brand settings to enable uploads.'));?></span>
				<input id="image-input" type="file" name="images[]" accept="<?php echo sendy_fm_escape(implode(',', array_map(function($extension){return '.'.$extension;}, $allowed_upload_extensions)));?>" multiple<?php echo $uploads_enabled ? '' : ' disabled';?>>
			</label>
		</form>

		<div class="panel">
			<div class="toolbar">
				<div class="breadcrumbs">
					<a href="?i=<?php echo $app_id;?>&amp;p=">/ <?php echo sendy_fm_escape(_('Uploads'));?></a>
					<?php
						$crumb_path = '';
						foreach($relative==='' ? array() : explode('/', $relative) as $crumb):
							$crumb_path = $crumb_path==='' ? $crumb : $crumb_path.'/'.$crumb;
					?> <span>/</span> <a href="?i=<?php echo $app_id;?>&amp;p=<?php echo rawurlencode($crumb_path);?>"><?php echo sendy_fm_escape($crumb);?></a><?php endforeach;?>
				</div>
				<div class="toolbar-group">
					<button type="button" class="btn small" id="select-all"><?php echo sendy_fm_escape(_('Select all'));?></button>
					<button type="button" class="btn small" id="unselect-all"><?php echo sendy_fm_escape(_('Unselect all'));?></button>
					<span id="selected-count">0 <?php echo sendy_fm_escape(_('selected'));?></span>
					<button type="button" class="btn danger small" id="delete-selected" disabled><?php echo sendy_fm_escape(_('Delete selected'));?></button>
				</div>
			</div>

			<form id="bulk-delete-form" method="post">
				<input type="hidden" name="i" value="<?php echo $app_id;?>">
				<input type="hidden" name="p" value="<?php echo sendy_fm_escape($relative);?>">
				<input type="hidden" name="page" value="<?php echo $page;?>">
				<input type="hidden" name="token" value="<?php echo sendy_fm_escape($csrf_token);?>">
				<input type="hidden" name="action" value="delete">
				<div class="table-wrap">
					<table>
						<thead><tr><th><input type="checkbox" id="master-check" aria-label="<?php echo sendy_fm_escape(_('Select all'));?>"></th><th><?php echo sendy_fm_escape(_('Name'));?></th><th><?php echo sendy_fm_escape(_('Size'));?></th><th><?php echo sendy_fm_escape(_('Modified'));?></th><th><?php echo sendy_fm_escape(_('Actions'));?></th></tr></thead>
						<tbody>
						<?php if($relative!==''):?>
							<tr><td></td><td class="name-cell"><span class="icon">↩</span><a class="name-link" href="?i=<?php echo $app_id;?>&amp;p=<?php echo rawurlencode($parent);?>">..</a></td><td></td><td></td><td></td></tr>
						<?php endif;?>
						<?php foreach($entries as $entry):
							$url = sendy_fm_public_url($app_id, $entry['relative']);
							$download_url = '?i='.$app_id.'&download='.rawurlencode($entry['relative']);
						?>
							<tr>
								<td><input class="item-check" type="checkbox" name="items[]" value="<?php echo sendy_fm_escape($entry['relative']);?>" aria-label="<?php echo sendy_fm_escape(sprintf(_('Select %s'), $entry['name']));?>"></td>
								<td class="name-cell">
									<span class="icon"><?php echo $entry['is_dir'] ? '📁' : ($entry['is_image'] ? '🖼' : '📄');?></span>
									<?php if($entry['is_dir']):?><a class="name-link" href="?i=<?php echo $app_id;?>&amp;p=<?php echo rawurlencode($entry['relative']);?>"><?php echo sendy_fm_escape($entry['name']);?></a><?php else:?><span class="name-link <?php echo $entry['dangerous'] ? 'danger-text' : '';?>"><?php echo sendy_fm_escape($entry['name']);?></span><?php endif;?>
									<?php if($entry['is_image']):?><img class="preview" loading="lazy" src="<?php echo sendy_fm_escape($url);?>" alt=""><?php endif;?>
								</td>
								<td><?php echo $entry['is_dir'] ? '—' : sendy_fm_escape(sendy_fm_format_size($entry['size']));?></td>
								<td><?php echo sendy_fm_escape(date('M j, Y g:i A', $entry['modified']));?></td>
								<td><div class="actions">
									<button type="button" class="btn small rename-btn" data-item="<?php echo sendy_fm_escape($entry['relative']);?>" data-name="<?php echo sendy_fm_escape($entry['name']);?>"><?php echo sendy_fm_escape(_('Rename'));?></button>
									<?php if(!$entry['is_dir'] && !$entry['dangerous']):?><button type="button" class="btn small copy-btn" data-url="<?php echo sendy_fm_escape($url);?>"><?php echo sendy_fm_escape(_('Copy link'));?></button><?php endif;?>
									<?php if(!$entry['is_dir']):?><a class="btn small" href="<?php echo sendy_fm_escape($download_url);?>"><?php echo sendy_fm_escape(_('Download'));?></a><?php endif;?>
									<button type="button" class="btn small delete-one-btn" data-item="<?php echo sendy_fm_escape($entry['relative']);?>"><?php echo sendy_fm_escape(_('Delete'));?></button>
								</div></td>
							</tr>
						<?php endforeach;?>
						<?php if($total_items===0):?><tr><td colspan="5" class="empty"><?php echo sendy_fm_escape(_('No files or folders yet. Upload a file to get started.'));?></td></tr><?php endif;?>
						</tbody>
					</table>
				</div>
			</form>
			<?php if($total_items>0):?>
			<div class="pagination">
				<div class="pagination-info"><?php echo sendy_fm_escape(sprintf(_('Showing %s–%s of %s items'), $page_first_item, $page_last_item, $total_items));?></div>
				<div class="pagination-links">
					<a class="btn small<?php echo $page<=1 ? ' disabled' : '';?>" href="<?php echo sendy_fm_escape($pagination_url.max(1, $page-1));?>"><?php echo sendy_fm_escape(_('Previous'));?></a>
					<?php
						$start_page = max(1, $page-2);
						$end_page = min($total_pages, $page+2);
						if($start_page>1):?><a class="btn small" href="<?php echo sendy_fm_escape($pagination_url.'1');?>">1</a><?php if($start_page>2):?><span>…</span><?php endif; endif;
						for($page_number=$start_page; $page_number<=$end_page; $page_number++):
					?><a class="btn small<?php echo $page_number===$page ? ' current' : '';?>" href="<?php echo sendy_fm_escape($pagination_url.$page_number);?>"><?php echo $page_number;?></a><?php endfor;
						if($end_page<$total_pages): if($end_page<$total_pages-1):?><span>…</span><?php endif;?><a class="btn small" href="<?php echo sendy_fm_escape($pagination_url.$total_pages);?>"><?php echo $total_pages;?></a><?php endif;?>
					<a class="btn small<?php echo $page>=$total_pages ? ' disabled' : '';?>" href="<?php echo sendy_fm_escape($pagination_url.min($total_pages, $page+1));?>"><?php echo sendy_fm_escape(_('Next'));?></a>
				</div>
			</div>
			<?php endif;?>
		</div>

		<form id="action-form" method="post" hidden>
			<input type="hidden" name="i" value="<?php echo $app_id;?>">
			<input type="hidden" name="p" value="<?php echo sendy_fm_escape($relative);?>">
			<input type="hidden" name="page" value="<?php echo $page;?>">
			<input type="hidden" name="token" value="<?php echo sendy_fm_escape($csrf_token);?>">
			<input type="hidden" name="action" value="">
			<input type="hidden" name="item" value="">
			<input type="hidden" name="name" value="">
		</form>
	</div>
	<div class="loading-overlay" id="loading-overlay" role="status" aria-live="polite" aria-hidden="true">
		<div class="loading-card"><div class="loading-spinner" aria-hidden="true"></div><div class="loading-text" id="loading-text"><?php echo sendy_fm_escape(_('Working…'));?></div></div>
	</div>
	<script>
	(function(){
		var overlay=document.getElementById('loading-overlay'), loadingText=document.getElementById('loading-text'), submitting=false;
		function submitWithLoading(form,message){
			if(submitting) return;
			submitting=true;
			loadingText.textContent=message;
			overlay.classList.add('active');
			overlay.setAttribute('aria-hidden','false');
			requestAnimationFrame(function(){setTimeout(function(){form.submit();},30);});
		}
		var uploadForm=document.getElementById('upload-form'), input=document.getElementById('image-input'), zone=document.getElementById('drop-zone'), uploadTitle=document.getElementById('upload-title'), uploadHelp=document.getElementById('upload-help'), uploading=false, uploadsEnabled=<?php echo $uploads_enabled ? 'true' : 'false';?>;
		function uploadFiles(files){
			if(!uploadsEnabled) return;
			if(uploading || !files || !files.length) return;
			var transfer=new DataTransfer();
			Array.prototype.forEach.call(files,function(file){transfer.items.add(file);});
			input.files=transfer.files;
			if(!input.files.length){
				uploadTitle.textContent='<?php echo addslashes(_('The browser could not attach the dropped image. Please click to choose it instead.'));?>';
				return;
			}
			uploading=true;
			zone.classList.remove('drag');
			zone.style.pointerEvents='none';
			uploadTitle.textContent='<?php echo addslashes(_('Uploading files…'));?>';
			uploadHelp.textContent='<?php echo addslashes(_('Please keep this window open.'));?>';
			submitWithLoading(uploadForm,'<?php echo addslashes(_('Uploading files…'));?>');
		}
		input.addEventListener('change',function(){
			if(!input.files.length || uploading) return;
			uploading=true;
			zone.style.pointerEvents='none';
			uploadTitle.textContent='<?php echo addslashes(_('Uploading files…'));?>';
			uploadHelp.textContent='<?php echo addslashes(_('Please keep this window open.'));?>';
			submitWithLoading(uploadForm,'<?php echo addslashes(_('Uploading files…'));?>');
		});
		['dragenter','dragover'].forEach(function(type){ zone.addEventListener(type,function(e){ e.preventDefault(); if(uploadsEnabled)zone.classList.add('drag'); }); });
		['dragleave','drop'].forEach(function(type){ zone.addEventListener(type,function(e){ e.preventDefault(); zone.classList.remove('drag'); }); });
		zone.addEventListener('drop',function(e){uploadFiles(e.dataTransfer.files);});

		var checks=[].slice.call(document.querySelectorAll('.item-check')), master=document.getElementById('master-check'), count=document.getElementById('selected-count'), deleteSelected=document.getElementById('delete-selected');
		function updateSelection(){ var selected=checks.filter(function(c){return c.checked;}).length; count.textContent=selected+' <?php echo addslashes(_('selected'));?>'; deleteSelected.disabled=selected===0; master.checked=checks.length>0&&selected===checks.length; master.indeterminate=selected>0&&selected<checks.length; }
		checks.forEach(function(c){c.addEventListener('change',updateSelection);});
		master.addEventListener('change',function(){checks.forEach(function(c){c.checked=master.checked;});updateSelection();});
		document.getElementById('select-all').addEventListener('click',function(){checks.forEach(function(c){c.checked=true;});updateSelection();});
		document.getElementById('unselect-all').addEventListener('click',function(){checks.forEach(function(c){c.checked=false;});updateSelection();});
		deleteSelected.addEventListener('click',function(){if(confirm('<?php echo addslashes(_('Delete the selected files and folders? This cannot be undone.'));?>'))submitWithLoading(document.getElementById('bulk-delete-form'),'<?php echo addslashes(_('Deleting selected items…'));?>');});

		var actionForm=document.getElementById('action-form');
		function submitAction(action,item,name,message){
			actionForm.elements.namedItem('action').value=action;
			actionForm.elements.namedItem('item').value=item||'';
			actionForm.elements.namedItem('name').value=name||'';
			submitWithLoading(actionForm,message||'<?php echo addslashes(_('Working…'));?>');
		}
		document.querySelectorAll('.rename-btn').forEach(function(btn){btn.addEventListener('click',function(){var name=prompt('<?php echo addslashes(_('Enter a new name'));?>',btn.dataset.name);if(name&&name!==btn.dataset.name)submitAction('rename',btn.dataset.item,name,'<?php echo addslashes(_('Renaming item…'));?>');});});
		document.querySelectorAll('.delete-one-btn').forEach(function(btn){btn.addEventListener('click',function(){if(confirm('<?php echo addslashes(_('Delete this item? This cannot be undone.'));?>')){var hidden=document.createElement('input');hidden.type='hidden';hidden.name='items[]';hidden.value=btn.dataset.item;actionForm.appendChild(hidden);submitAction('delete','','','<?php echo addslashes(_('Deleting item…'));?>');}});});
		function copyText(value){if(navigator.clipboard&&window.isSecureContext)return navigator.clipboard.writeText(value);var area=document.createElement('textarea');area.value=value;area.style.position='fixed';area.style.opacity='0';document.body.appendChild(area);area.select();document.execCommand('copy');document.body.removeChild(area);return Promise.resolve();}
		document.querySelectorAll('.copy-btn').forEach(function(btn){btn.addEventListener('click',function(){var original=btn.textContent;copyText(btn.dataset.url).then(function(){btn.textContent='<?php echo addslashes(_('Copied'));?>';setTimeout(function(){btn.textContent=original;},1200);});});});
		document.getElementById('new-folder-btn').addEventListener('click',function(){var name=prompt('<?php echo addslashes(_('Folder name'));?>');if(name)submitAction('new_folder','',name,'<?php echo addslashes(_('Creating folder…'));?>');});

		document.querySelectorAll('.name-cell .preview').forEach(function(preview){
			var cell=preview.closest('.name-cell');
			function positionPreview(){
				var cellRect=cell.getBoundingClientRect(), previewRect=preview.getBoundingClientRect(), gap=8, edge=12;
				var left=Math.min(cellRect.left+45,window.innerWidth-previewRect.width-edge);
				var top=cellRect.bottom+gap;
				if(top+previewRect.height>window.innerHeight-edge) top=cellRect.top-previewRect.height-gap;
				preview.style.left=Math.max(edge,left)+'px';
				preview.style.top=Math.max(edge,top)+'px';
			}
			cell.addEventListener('mouseenter',positionPreview);
			preview.addEventListener('load',function(){if(cell.matches(':hover'))positionPreview();});
		});
	})();
	</script>
</body>
</html>
