yf_file_manager admin moduleFile manager module.
| File | plugins/sys/admin_modules/yf_file_manager.class.php |
|---|---|
| Use | admin URL ?object=file_manager |
A site sets them in its config with $PROJECT_CONF['file_manager']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$ALLOWED_DIRSarray |
[] |
Directories the sandbox allows; empty means [PROJECT_PATH, UPLOADS_PATH], resolved at run time |
$DENIED_EXTENSIONSarray |
|
Extensions (any dotted segment of the name) the sandbox refuses to write, compared case-insensitively |
$SANDBOXbool |
false |
When true: super-admins only, every request path must lie inside ALLOWED_DIRS, writes refuse DENIED_EXTENSIONS |
$_copy_dir_deepnessint |
2 |
|
$_default_chmodint |
511 |
@conf_skip |
$_default_emailstring |
'' |
|
$_exclude_patternstring |
'#\\.(svn|git)#' |
@conf_skip Exclude files pattern |
$_include_patternstring |
'' |
@conf_skip Include files pattern |
$color_dirstring |
'#0066cc' |
Color of the subdirs |
$color_filestring |
'#00aa66' |
Color of the files |
$color_zipstring |
'navy' |
Color of the ZIP archives |
public function chmod_item()
public function chmod_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$dir_name = urldecode($_POST['dir_name']);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
if ( ! $_POST['new_chmod']) {
$_POST['new_chmod'] = '755';
}
$_old_dir_name = str_replace('\\', '/', getcwd());
chdir($dir_name);
foreach ((array) $_POST as $k => $v) {
$tmp = substr($k, 0, 2);
if ($tmp != 'd_' && $tmp != 'f_') {
continue;
}
$file_name = str_replace('\\', '/', $dir_name . '/' . $v);
if ($this->SANDBOX && $this->_sandbox_file($file_name) === '') {
continue;
}
chmod($file_name, '0' . $_POST['new_chmod']);
}
chdir($_old_dir_name);
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . ($_POST['dir_name'] ?? null) . _add_get(['dir_name']));
}
public function copy_item()
public function copy_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$dir_name = urldecode($_POST['dir_name']);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
$items_to_copy = [];
$items = null;
foreach ((array) $_POST as $k => $v) {
$tmp = substr($k, 0, 2);
if ($tmp != 'd_' && $tmp != 'f_') {
continue;
}
if ($tmp == 'd_') {
$color = $this->color_dir;
$type = strtoupper(t('directory'));
} elseif ($tmp == 'f_') {
$color = $this->color_file;
$type = strtoupper(t('file'));
}
$file_name = str_replace('\\', '/', $dir_name . '/' . $v);
if ($this->SANDBOX && ($file_name = $this->_confine($file_name)) === false) {
continue;
}
$items_to_copy[] = $tmp . $file_name;
$replace2 = [
'color' => $color,
'file_name' => $file_name,
'type' => $type,
];
$items .= tpl()->parse(($_GET['object'] ?? null) . '/copy_item', $replace2);
}
$replace = [
'form_action' => './?object=' . ($_GET['object'] ?? null) . '&action=copy_item2&dir_name=' . ($_POST['dir_name'] ?? null) . _add_get(['dir_name']),
'items' => $items,
'items_to_copy' => urlencode($this->_pack_items($items_to_copy)),
'dest_dirs' => $this->_get_all_dirs($this->SANDBOX && $this->_confine(dirname($dir_name)) === false ? $dir_name : dirname($dir_name)),
'back' => back('./?object=' . $_GET['object'] . '&dir_name=' . $_POST['dir_name'] . _add_get(['dir_name'])),
];
return tpl()->parse(($_GET['object'] ?? null) . '/copy', $replace);
}
public function copy_item2()
Copy selected items into selected destination folders.
public function copy_item2()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$body = null;
if ($_POST['items_to_copy']) {
$items = $this->_unpack_items(urldecode($_POST['items_to_copy']));
$items_to_copy = null;
$dest_folders = null;
foreach ((array) $items as $k => $v) {
$tmp = substr($v, 0, 2);
if ($tmp == 'd_' || $tmp == 'f_') {
$path = substr($v, 2);
if ($this->SANDBOX && ($path = $this->_confine($path)) === false) {
continue;
}
$items_to_copy[] = $path;
}
}
foreach ((array) $_POST as $k => $v) {
$tmp = substr($k, 0, 2);
if ($tmp == 'd_' || $tmp == 'f_') {
$dest = urldecode(substr($k, 2));
if ($this->SANDBOX && ($dest = $this->_confine($dest)) === false) {
continue;
}
$dest_folders[] = $dest;
}
}
if (count((array) $items_to_copy)) {
if (count((array) $dest_folders)) {
foreach ((array) $items_to_copy as $k => $v) {
if ($this->SANDBOX && (is_link($v) || $this->_is_denied_name($v) || (is_dir($v) && $this->_dir_has_unsafe($v)))) {
continue;
}
if (is_dir($v)) {
foreach ((array) $dest_folders as $k1 => $v1) {
// Check if dirs are different
// !! Need to add checking of parentess of dirs (to avoid infinite copy recursion)
if ($v != $v1) {
$new_name = $v1 . '/' . basename($v);
if ($this->SANDBOX && is_link($new_name)) {
continue;
}
mkdir($new_name, 0777);
_class('dir')->copy_dir($v, $new_name);
}
}
} else {
foreach ((array) $dest_folders as $k1 => $v1) {
if ($this->SANDBOX && is_link($v1 . '/' . basename($v))) {
continue;
}
copy($v, $v1 . '/' . basename($v));
}
}
}
js_redirect('./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . ($_GET['dir_name'] ?? null) . _add_get(['dir_name']));
} else {
$body .= t('please_select_destinatin_folders');
}
} else {
$body .= t('please_select_items_to_copy');
}
} else {
$body .= t('please_select_items_to_copy');
}
$body .= back('./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . ($_GET['dir_name'] ?? null));
return $body;
}
public function delete_item()
public function delete_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$dir_name = urldecode($_POST['dir_name']);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
foreach ((array) $_POST as $k => $v) {
$file_name = str_replace('\\', '/', $dir_name . '/' . $v);
$tmp = substr($k, 0, 2);
if ($tmp != 'd_' && $tmp != 'f_') {
continue;
}
if ( ! file_exists($file_name)) {
continue;
}
if ($this->SANDBOX && (is_link($file_name) || ($file_name = $this->_confine($file_name)) === false || in_array($file_name, $this->_allowed_dirs(), true) || (is_dir($file_name) && $this->_dir_has_unsafe($file_name)))) {
continue;
}
if (is_dir($file_name)) {
_class('dir')->delete_dir($file_name);
@closedir($file_name);
@rmdir($file_name);
} else {
@unlink($file_name);
}
}
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . ($_POST['dir_name'] ?? null) . _add_get(['dir_name']));
}
public function download_item()
public function download_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$dir_name = urldecode($_POST['dir_name']);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
$_old_dir_name = str_replace('\\', '/', getcwd());
chdir($dir_name);
foreach ((array) $_POST as $k => $v) {
$file_name = str_replace('\\', '/', $dir_name . '/' . $v);
$tmp = substr($k, 0, 2);
if ($tmp != 'd_' && $tmp != 'f_') {
continue;
}
$this->SANDBOX && $file_name = (string) $this->_confine($file_name);
if (is_readable($file_name) && is_file($file_name)) {
clearstatcache();
header('Content-Type: application/force-download; name="' . $v . '"');
header('Content-Type: text/plain');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file_name));
header('Content-Disposition: attachment; filename="' . $v . '"');
readfile($file_name);
main()->NO_GRAPHICS = true;
exit();
}
chdir($_old_dir_name);
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . ($_POST['dir_name'] ?? null) . _add_get(['dir_name']));
}
}
public function edit()
public function edit()
{
return $this->edit_item();
}
public function edit_item()
public function edit_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
// TODO: save file revision to db on each save
if ( ! empty($_GET['id'])) {
$file_name = urldecode(($_GET['id'] ?? ''));
$file_path = $file_name;
$dir_name = dirname($file_path);
} else {
$name = null;
foreach ((array) $_REQUEST as $k => $v) {
$tmp = substr($k, 0, 2);
if ($tmp == 'd_' || $tmp == 'f_') {
$name = $v;
break;
}
}
$dir_name = urldecode(($_REQUEST['dir_name'] ?? ''));
$file_name = str_replace('\\', '/', $dir_name . '/' . $name);
$file_path = $file_name;
}
if ($this->SANDBOX) {
if (($file_path = $this->_confine($file_path)) === false) {
return $this->_sandbox_deny();
}
$file_name = $file_path;
}
if (main()->is_post()) {
$file_name = urldecode($_GET['file_name']);
if ($this->SANDBOX && ($file_name = $this->_sandbox_file($file_name)) === '') {
return $this->_sandbox_deny();
}
file_put_contents($file_name, $_POST['file_text_hidden']);
return js_redirect('/@object/show/' . urlencode($_GET['dir_name']));
}
$_tmp_array = [];
$tmp_path = '/';
foreach ((array) explode('/', dirname($file_name)) as $_folder) {
if ($_folder) {
$tmp_path .= $_folder . '/';
$_tmp_array[] = a('/@object/show/' . urlencode($tmp_path), $_folder);
}
}
if ($_tmp_array) {
$file_name = '/' . implode('/', $_tmp_array) . '/' . _prepare_html(basename($file_name), 0);
}
$file_text = _prepare_html(file_get_contents($file_path), 0);
$replace = [
'form_action' => url('/@object/@action/' . urlencode($file_path)),
'back_link' => url('/@object/show/' . urlencode(($_REQUEST['dir_name'] ?? ''))),
];
$div_id = 'editor_html';
$hidden_id = 'file_text_hidden';
$ace_mode = common()->get_file_ext($file_path);
$ace_mode == 'stpl' && $ace_mode = 'html';
jquery('
var h = $(window).height() - $(".navbar").height() - $("h4").height() * 2 - $("[type=submit]").height() * 5
$("#' . $div_id . '").height(h);
');
return '<h4>Edit: ' . $file_name . '</h4>' .
form($replace, [
'data-onsubmit' => '$(this).find("#' . $hidden_id . '").val( $("#' . $div_id . '").data("ace_editor").session.getValue() );',
])
->container('<div id="' . $div_id . '" style="width: 100%; min-height: 500px;">' . $file_text . '</div>', '', [
'id' => $div_id,
'wide' => 1,
'ace_editor' => ['mode' => $ace_mode, 'hScrollBarAlwaysVisible' => false, 'vScrollBarAlwaysVisible' => false],
])
->hidden($hidden_id)
->save_and_back();
}
public function email_item()
public function email_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$dir_name = urldecode($_POST['dir_name']);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
$_old_dir_name = str_replace('\\', '/', getcwd());
chdir($dir_name);
$attach = [];
foreach ((array) $_POST as $k => $v) {
$file_name = str_replace('\\', '/', $dir_name . '/' . $v);
$tmp = substr($k, 0, 2);
if ($tmp != 'd_' && $tmp != 'f_') {
continue;
}
$this->SANDBOX && $file_name = (string) $this->_confine($file_name);
if (file_exists($file_name) && is_readable($file_name)) {
$attach[] = $v;
}
}
if (count((array) $attach)) {
$email_from = 'yfix.dev auto-sender';
if ( ! ($_POST['target_email'] ?? null)) {
$email_to = '[email protected]';
} else {
$email_to = ($_POST['target_email'] ?? null);
}
$to_name = 'auto-sender destination';
$subject = 'yfix.dev auto-sender generated email';
$HTML = 'see attachment';
$TEXT = 'see attachment';
$result = common()->send_mail('PHP-Mailer', $email_from, $email_to, $to_name, $subject, $TEXT, $HTML, $attach);
}
chdir($_old_dir_name);
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . ($_POST['dir_name'] ?? null) . _add_get(['dir_name']));
}
public function make_dir()
public function make_dir()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$body = null;
if ($_POST['verify'] && $_POST['name']) {
$new_dir = urldecode($_POST['dir_name']) . '/' . $_POST['name'];
$this->SANDBOX && $new_dir = $this->_sandbox_file($new_dir, false);
if ($new_dir !== '' && ! @file_exists($new_dir)) {
mkdir($new_dir, 0777);
}
}
$body .= $this->show();
return $body;
}
public function make_zip_item()
public function make_zip_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$dir_name = urldecode($_POST['dir_name']);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
if ( ! $_POST['new_zip_name']) {
$_POST['new_zip_name'] = 'tmp_1234';
}
$new_zip_name = $dir_name . '/' . ($_POST['new_zip_name'] ?? null) . '.zip';
$_old_dir_name = str_replace('\\', '/', getcwd());
chdir($dir_name);
$item_list = [];
foreach ((array) $_POST as $k => $v) {
$tmp = substr($k, 0, 2);
if ($tmp != 'd_' && $tmp != 'f_') {
continue;
}
$file_name = str_replace('\\', '/', $dir_name . '/' . $v);
if ($this->SANDBOX && $this->_confine($file_name) === false) {
continue;
}
if (file_exists($file_name) && is_readable($file_name)) {
$item_list[] = $v;
}
}
$_old_dir_name = str_replace('\\', '/', getcwd());
chdir($_old_dir_name);
// TODO: replace making ZIP with php built-in http://php.net/manual/en/zip.examples.php
return js_redirect('./?object=' . $_GET['object'] . '&dir_name=' . $_POST['dir_name'] . _add_get(['dir_name']));
}
public function show()
public function show()
{
$items = $total_files_size = null;
// TODO: show preview if file is image
// TODO: doubleclick should edit file
// TODO: buttons near eacj table row (edit/show revisions)
// TODO: show revisions
// TODO: make table condensed
// TODO: completely avoid tpl(): speedup, code in one place
// TODO: overall cleanup and improve UI from bootstrap
// TODO: left area show directory tree with ajax
// TODO: ajax file/image upload
// TODO: wanr if we cannot edit this file _before_ edit (inside table listing)
// TODO: rewrite JS into angularjs for UI interanction
// TODO: copy_item rewrite to use infinite recursion
// TODO: add merge dirs with dialog for new name
// TODO: test on large number of files inside dir
// TODO: add ability to allow some paths for specific admiin groups (fine grained internal permissions or ACL)
// TODO: add ajax calculate dirs size (one-by-one to not kill server performance)
// TODO: add filesystem stats (disks usage, mounted filesystems, iostat if possible, etc)
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$path_info = pathinfo($_SERVER['SCRIPT_FILENAME']);
$_old_dir_name = str_replace('\\', '/', getcwd());
$_dir_name = ($_GET['dir_name'] ?? null) ?: ($_GET['id'] ?? null);
if ($_dir_name) {
$dir_name = urldecode($_dir_name);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
chdir($dir_name);
} else {
$dir_name = $path_info['dirname'];
if ($this->SANDBOX) {
if (($dir_name = $this->_confine($dir_name) ?: current($this->_allowed_dirs())) === false) {
return $this->_sandbox_deny();
}
chdir($dir_name);
}
}
$path_info_up = pathinfo($dir_name);
$cur_dir_name = str_replace('\\', '/', getcwd());
$encoded_cur_dir = urlencode($cur_dir_name);
$dir_contents = $this->_get_dir_contents($cur_dir_name);
chdir($_old_dir_name);
$items = null;
$total_files_size = null;
if ( ! empty($dir_contents['dirs']) || ! empty($dir_contents['files'])) {
foreach ((array) $dir_contents['dirs'] as $k => $v) {
$replace2 = [
'encoded_name' => urlencode($v),
'name' => $v,
'go_into_link' => './?object=' . ($_GET['object'] ?? null) . '&dir_name=' . urlencode($dir_name . '/' . $v) . _add_get(['dir_name']),
'color' => $this->color_dir,
'm_date' => date('Y-m-d H:i:s', filemtime($cur_dir_name . '/' . $v)),
'perms' => $this->_get_perms($cur_dir_name . '/' . $v),
'size' => 0,
'is_dir' => 1,
'is_file' => 0,
];
$items .= tpl()->parse(($_GET['object'] ?? null) . '/item', $replace2);
}
foreach ((array) $dir_contents['files'] as $k => $v) {
if (substr($v, -3) == 'zip') {
$color = $this->color_zip;
} else {
$color = $this->color_file;
}
$file_size = filesize($cur_dir_name . '/' . $v);
$total_files_size += $file_size;
$replace2 = [
'encoded_name' => urlencode($v),
'name' => $v,
'go_into_link' => '',
'color' => $color,
'm_date' => date('Y-m-d H:i:s', filemtime($cur_dir_name . '/' . $v)),
'perms' => $this->_get_perms($cur_dir_name . '/' . $v),
'size' => $file_size,
'is_dir' => 0,
'is_file' => 1,
];
$items .= tpl()->parse(($_GET['object'] ?? null) . '/item', $replace2);
}
}
clearstatcache();
$_tmp_path = '';
$_tmp_array = [];
foreach ((array) explode('/', $cur_dir_name) as $_folder) {
$_tmp_path .= $_folder . '/';
$_tmp_array[] = '<a href="./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . urlencode($_tmp_path) . _add_get(['dir_name']) . '" class="btn btn-mini btn-xs">' . _prepare_html($_folder) . '</a>';
}
if ($_tmp_array) {
$cur_dir_name = implode('/', $_tmp_array);
}
$replace = [
'form_action' => './?object=' . ($_GET['object'] ?? null) . _add_get(['dir_name']) . '&action=',
'upload_form_action' => './?object=' . ($_GET['object'] ?? null) . '&action=upload_file&dir_name=' . $encoded_cur_dir . _add_get(['dir_name']),
'mkdir_form_action' => './?object=' . ($_GET['object'] ?? null) . '&action=make_dir&dir_name=' . $encoded_cur_dir . _add_get(['dir_name']),
'cur_dir_name' => $cur_dir_name,
'encoded_dir_name' => $encoded_cur_dir,
'go_up_level_link' => './?object=' . ($_GET['object'] ?? null) . '&dir_name=' . urlencode($path_info_up['dirname']) . _add_get(['dir_name']),
'go_home_link' => './?object=' . ($_GET['object'] ?? null) . _add_get(['dir_name']),
'total_files_size' => (int) $total_files_size,
'total_files' => (int) (count((array) $dir_contents['files'])),
'total_dirs' => (int) (count((array) $dir_contents['dirs'])),
'default_zip_name' => 'tmp12345',
'default_email' => conf('webmaster_mail'),
'default_chmod' => 755,
'items' => $items,
];
return tpl()->parse(($_GET['object'] ?? null) . '/main', $replace);
}
public function unzip_item()
public function unzip_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$dir_name = urldecode($_POST['dir_name']);
if ($this->SANDBOX && ($dir_name = $this->_confine($dir_name)) === false) {
return $this->_sandbox_deny();
}
$_old_dir_name = str_replace('\\', '/', getcwd());
foreach ((array) $_POST as $k => $name) {
$tmp = substr($k, 0, 2);
if ($tmp != 'd_' && $tmp != 'f_') {
continue;
}
$ext = array_pop(explode('.', $name));
if ($ext != 'zip') {
continue;
}
$file_name = str_replace('\\', '/', $dir_name . '/' . $name);
$extraction_dir = $dir_name . '/' . substr($name, 0, -4);
if ($this->SANDBOX && ($this->_confine($file_name) === false || $this->_sandbox_file($extraction_dir, false) === '')) {
continue;
}
if ( ! file_exists($extraction_dir)) {
mkdir($extraction_dir, 0777);
}
chdir($extraction_dir);
// TODO: check or replace pclzip
}
chdir($_old_dir_name);
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&dir_name=' . ($_POST['dir_name'] ?? null) . _add_get(['dir_name']));
}
public function upload_file()
public function upload_file()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
$body = null;
if (($_POST['verify'] ?? null)) {
if (($_POST['name'] ?? null)) {
$Name = ($_POST['name'] ?? null);
} else {
$Name = ($_FILES['file']['name'] ?? null);
}
$fileName = urldecode($_POST['dir_name']) . '/' . $Name;
$this->SANDBOX && $fileName = $this->_sandbox_file($fileName, false);
if ($fileName === '' || ! move_uploaded_file($_FILES['file']['tmp_name'], $fileName)) {
$body .= error_back();
} else {
$body .= t('save_successful') . '<br />' . PHP_EOL;
}
}
$body .= $this->show();
return $body;
}
public function view()
public function view()
{
return $this->view_item();
}
public function view_item()
public function view_item()
{
if ( ! $this->_sandbox_user_ok()) {
return $this->_sandbox_deny();
}
if ( ! empty($_GET['id'])) {
$file_name = urldecode(($_GET['id'] ?? ''));
$file_path = $file_name;
$dir_name = dirname($file_path);
} else {
$name = null;
foreach ((array) $_REQUEST as $k => $v) {
$tmp = substr($k, 0, 2);
if ($tmp == 'd_' || $tmp == 'f_') {
$name = $v;
break;
}
}
$dir_name = urldecode(($_REQUEST['dir_name'] ?? ''));
$file_name = str_replace('\\', '/', $dir_name . '/' . $name);
$file_path = $file_name;
}
if ($this->SANDBOX) {
if (($file_path = $this->_confine($file_path)) === false) {
return $this->_sandbox_deny();
}
$file_name = $file_path;
}
$_tmp_array = [];
$tmp_path = '/';
foreach ((array) explode('/', dirname($file_name)) as $_folder) {
if ($_folder) {
$tmp_path .= $_folder . '/';
$_tmp_array[] = a('/@object/show/' . urlencode($tmp_path), $_folder);
}
}
if ($_tmp_array) {
$file_name = '/' . implode('/', $_tmp_array) . '/' . _prepare_html(basename($file_name), 0);
}
$file_text = _prepare_html(file_get_contents($file_path), 0);
$replace = [
'back_link' => url('/@object/show/' . urlencode(($_REQUEST['dir_name'] ?? ''))),
];
$div_id = 'editor_html';
$hidden_id = 'file_text_hidden';
return '<h4>View: ' . $file_name . '</h4>' .
form($replace)
->container('<div id="' . $div_id . '" style="width: 90%; height: 500px;">' . $file_text . '</div>', '', [
'id' => $div_id,
'wide' => 1,
'ace_editor' => ['mode' => common()->get_file_ext($file_path)],
])
->hidden($hidden_id);
}
_ hooks and helpers; not routable as page actionspublic function _allowed_dirs()
Returns array Real paths of the directories the sandbox allows
public function _allowed_dirs()
{
$dirs = (array) $this->ALLOWED_DIRS;
if ( ! $dirs) {
defined('PROJECT_PATH') && $dirs[] = PROJECT_PATH;
defined('UPLOADS_PATH') && $dirs[] = UPLOADS_PATH;
}
$result = [];
foreach ($dirs as $dir) {
$real = realpath($dir);
$real !== false && $result[] = str_replace('\\', '/', $real);
}
return $result;
}
public function _confine($path, $must_exist = true)
Same as _real() but returns false instead of throwing; a path that does not exist yet is confined by its parent dir.
public function _confine($path, $must_exist = true)
{
try {
if ($must_exist) {
return $this->_real($path);
}
$path = rtrim(str_replace('\\', '/', (string) $path), '/');
$base = basename($path);
if ($base === '' || $base === '.' || $base === '..') {
return false;
}
$new = rtrim($this->_real(dirname($path)), '/') . '/' . $base;
return (file_exists($new) || is_link($new)) ? $this->_real($new) : $new;
} catch (Throwable $e) {
return false;
}
}
public function _dir_has_unsafe($dir)
Sandbox only: true when a directory holds a denied name or a symlink (recursive copy and delete follow links), or cannot be read.
public function _dir_has_unsafe($dir)
{
try {
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
foreach ($it as $file) {
if ($file->isLink() || $this->_is_denied_name($file->getFilename())) {
return true;
}
}
} catch (Throwable $e) {
return true;
}
return false;
}
public function _get_all_dirs($dir_name = '', $level = 1)
Show multi-level tree with folders inside.
public function _get_all_dirs($dir_name = '', $level = 1)
{
$body = null;
if ( ! file_exists($dir_name)) {
return;
}
$_old_dir_name = str_replace('\\', '/', getcwd());
chdir($dir_name);
$dir_contents = $this->_get_dir_contents($dir_name);
chdir($_old_dir_name);
$body = null;
foreach ((array) $dir_contents['dirs'] as $cur_name) {
$dir_next = $dir_name . '/' . $cur_name;
$replace = [
'color' => $this->color_dir,
'name' => $cur_name,
'encoded_name' => urlencode($dir_next),
'padding' => ($level - 1) * 30,
];
$body .= tpl()->parse(($_GET['object'] ?? null) . '/copy_dir_item', $replace);
if ($level < $this->_copy_dir_deepness) {
$body .= $this->_get_all_dirs($dir_next, $level + 1);
}
}
return $body;
}
public function _get_dir_contents($abs_dir_name)
Return sorted directory contents.
public function _get_dir_contents($abs_dir_name)
{
$contents = [
'dirs' => [],
'files' => [],
];
$handle = opendir($abs_dir_name);
while (false !== ($tmp_file = readdir($handle))) {
if ($tmp_file == '.' || $tmp_file == '..') {
continue;
}
if ( ! empty($this->_include_pattern)) {
if ( ! preg_match($this->_include_pattern . 'ims', $tmp_file)) {
continue;
}
}
if ( ! empty($this->_exclude_pattern)) {
if (preg_match($this->_exclude_pattern . 'ims', $tmp_file)) {
continue;
}
}
if (is_dir($tmp_file)) {
$contents['dirs'][] = $tmp_file;
} elseif (is_file($tmp_file)) {
$contents['files'][] = $tmp_file;
}
}
natsort($contents['dirs']);
natsort($contents['files']);
return $contents;
}
public function _get_perms($file_name)
public function _get_perms($file_name)
{
return substr(sprintf('%o', fileperms($file_name)), -3);
}
public function _is_denied_name($name)
public function _is_denied_name($name)
{
$name = rtrim(basename((string) $name), ". \t");
if (strpos($name, "\0") !== false) {
return true;
}
$parts = explode('.', strtolower($name));
array_shift($parts);
return (bool) array_intersect($parts, array_map('strtolower', (array) $this->DENIED_EXTENSIONS));
}
public function _pack_items($items)
Items selected for copying travel through a hidden form field; base64 keeps any byte sequence in file names.
public function _pack_items($items)
{
return json_encode(array_map('base64_encode', array_values((array) $items)));
}
public function _real($path)
Real path of an existing path, throws when it is missing or outside of the allowed dirs.
public function _real($path)
{
if ( ! is_string($path) || ! strlen($path) || strpos($path, "\0") !== false) {
throw new Exception('Bad path');
}
$temp = realpath($path);
if ($temp === false) {
throw new Exception('Path does not exist: ' . $path);
}
$temp = str_replace('\\', '/', $temp);
foreach ($this->_allowed_dirs() as $dir) {
if ($temp === $dir || strpos($temp, rtrim($dir, '/') . '/') === 0) {
return $temp;
}
}
throw new Exception('Path is not inside allowed dirs: ' . $temp);
}
public function _unpack_items($packed)
public function _unpack_items($packed)
{
$items = json_decode((string) $packed, true);
if ( ! is_array($items)) {
return [];
}
$result = [];
foreach ($items as $item) {
is_string($item) && $result[] = base64_decode($item, true);
}
return array_filter($result, 'is_string');
}