mirror of
https://github.com/netfun2000/pheditor.git
synced 2026-02-27 09:45:14 +08:00
1077 lines
42 KiB
PHP
Executable File
1077 lines
42 KiB
PHP
Executable File
<?php
|
|
|
|
/*
|
|
* Pheditor
|
|
* PHP file editor
|
|
* Hamid Samak
|
|
* https://github.com/hamidsamak/pheditor
|
|
* Release under MIT license
|
|
*/
|
|
|
|
define('PASSWORD', 'c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec');
|
|
define('DS', DIRECTORY_SEPARATOR);
|
|
define('MAIN_DIR', __DIR__);
|
|
define('VERSION', '2.0.0');
|
|
define('LOG_FILE', MAIN_DIR . DS . '.phedlog');
|
|
define('SHOW_PHP_SELF', false);
|
|
define('SHOW_HIDDEN_FILES', false);
|
|
define('ACCESS_IP', '');
|
|
define('HISTORY_PATH', MAIN_DIR . DS . '.phedhistory');
|
|
define('MAX_HISTORY_FILES', 5);
|
|
define('WORD_WRAP', true);
|
|
define('PERMISSIONS', 'newfile,newdir,editfile,deletefile,deletedir,renamefile,renamedir,changepassword,uploadfile'); // empty means all
|
|
define('PATTERN_FILES', '/^(.*)\.(txt|php|htm|html|js|css|tpl|md|xml|json)$/i'); // empty means no pattern
|
|
define('PATTERN_DIRECTORIES', '/^((?!backup).)*$/i'); // empy means no pattern
|
|
|
|
if (empty(ACCESS_IP) === false && ACCESS_IP != $_SERVER['REMOTE_ADDR']) {
|
|
die('Your IP address is not allowed to access this page.');
|
|
}
|
|
|
|
if (file_exists(LOG_FILE)) {
|
|
$log = unserialize(file_get_contents(LOG_FILE));
|
|
|
|
if (empty($log)) {
|
|
$log = [];
|
|
}
|
|
|
|
if (isset($log[$_SERVER['REMOTE_ADDR']]) && $log[$_SERVER['REMOTE_ADDR']]['num'] > 3 && time() - $log[$_SERVER['REMOTE_ADDR']]['time'] < 86400) {
|
|
die('This IP address is blocked due to unsuccessful login attempts.');
|
|
}
|
|
|
|
foreach ($log as $key => $value) {
|
|
if (time() - $value['time'] > 86400) {
|
|
unset($log[$key]);
|
|
|
|
$log_updated = true;
|
|
}
|
|
}
|
|
|
|
if (isset($log_updated)) {
|
|
file_put_contents(LOG_FILE, serialize($log));
|
|
}
|
|
}
|
|
|
|
session_set_cookie_params(86400, dirname($_SERVER['REQUEST_URI']));
|
|
session_name('pheditor');
|
|
session_start();
|
|
|
|
if (empty(PASSWORD) === false && (isset($_SESSION['pheditor_admin']) === false || $_SESSION['pheditor_admin'] !== true)) {
|
|
if (isset($_POST['pheditor_password']) && empty($_POST['pheditor_password']) === false) {
|
|
if (hash('sha512', $_POST['pheditor_password']) === PASSWORD) {
|
|
$_SESSION['pheditor_admin'] = true;
|
|
|
|
redirect();
|
|
} else {
|
|
$error = 'The entry password is not correct.';
|
|
|
|
$log = file_exists(LOG_FILE) ? unserialize(file_get_contents(LOG_FILE)) : array();
|
|
|
|
if (isset($log[$_SERVER['REMOTE_ADDR']]) === false) {
|
|
$log[$_SERVER['REMOTE_ADDR']] = array('num' => 0, 'time' => 0);
|
|
}
|
|
|
|
$log[$_SERVER['REMOTE_ADDR']]['num'] += 1;
|
|
$log[$_SERVER['REMOTE_ADDR']]['time'] = time();
|
|
|
|
file_put_contents(LOG_FILE, serialize($log));
|
|
}
|
|
} else if (isset($_POST['action'])) {
|
|
header('HTTP/1.0 403 Forbidden');
|
|
|
|
die('Your session has expired.');
|
|
}
|
|
|
|
die('<title>Pheditor</title><form method="post"><div style="text-align:center"><h1><a href="http://github.com/hamidsamak/pheditor" target="_blank" title="PHP file editor" style="color:#444;text-decoration:none" tabindex="3">Pheditor</a></h1>' . (isset($error) ? '<p style="color:#dd0000">' . $error . '</p>' : null) . '<input id="pheditor_password" name="pheditor_password" type="password" value="" placeholder="Password…" tabindex="1"><br><br><input type="submit" value="Login" tabindex="2"></div></form><script type="text/javascript">document.getElementById("pheditor_password").focus();</script>');
|
|
}
|
|
|
|
if (isset($_GET['logout'])) {
|
|
unset($_SESSION['pheditor_admin']);
|
|
|
|
redirect();
|
|
}
|
|
|
|
$permissions = explode(',', PERMISSIONS);
|
|
$permissions = array_map('trim', $permissions);
|
|
$permissions = array_filter($permissions);
|
|
|
|
if (count($permissions) < 1) {
|
|
$permissions = explode(',', 'newfile,newdir,editfile,deletefile,deletedir,renamefile,renamedir,changepassword');
|
|
}
|
|
|
|
if (isset($_POST['action'])) {
|
|
if (isset($_POST['file']) && empty($_POST['file']) === false) {
|
|
if (empty(PATTERN_FILES) === false && !preg_match(PATTERN_FILES, $_POST['file'])) {
|
|
die('danger|Invalid file pattern');
|
|
}
|
|
|
|
if (strpos($_POST['file'], '../') !== false || strpos($_POST['file'], '..\'') !== false) {
|
|
die('danger|Invalid file path');
|
|
}
|
|
}
|
|
|
|
switch ($_POST['action']) {
|
|
case 'open':
|
|
$_POST['file'] = urldecode($_POST['file']);
|
|
|
|
if (isset($_POST['file']) && file_exists(MAIN_DIR . $_POST['file'])) {
|
|
echo file_get_contents(MAIN_DIR . $_POST['file']);
|
|
}
|
|
break;
|
|
|
|
case 'save':
|
|
$file = MAIN_DIR . $_POST['file'];
|
|
|
|
if (isset($_POST['file']) && isset($_POST['data']) && (file_exists($file) === false || is_writable($file))) {
|
|
if (file_exists($file) === false) {
|
|
if (in_array('newfile', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
file_put_contents($file, $_POST['data']);
|
|
|
|
echo 'success|File saved successfully';
|
|
} else if (is_writable($file) === false) {
|
|
echo 'danger|File is not writable';
|
|
} else {
|
|
if (in_array('editfile', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
if (file_exists($file)) {
|
|
file_to_history($file);
|
|
}
|
|
|
|
file_put_contents($file, $_POST['data']);
|
|
|
|
echo 'success|File saved successfully';
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'make-dir':
|
|
if (in_array('newdir', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
$dir = MAIN_DIR . $_POST['dir'];
|
|
|
|
if (file_exists($dir) === false) {
|
|
mkdir($dir);
|
|
|
|
echo 'success|Directory created successfully';
|
|
} else {
|
|
echo 'warning|Directory already exists';
|
|
}
|
|
break;
|
|
|
|
case 'reload':
|
|
echo files(MAIN_DIR);
|
|
break;
|
|
|
|
case 'password':
|
|
if (in_array('changepassword', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
if (isset($_POST['password']) && empty($_POST['password']) === false) {
|
|
$contents = file(__FILE__);
|
|
|
|
foreach ($contents as $key => $line) {
|
|
if (strpos($line, 'define(\'PASSWORD\'') !== false) {
|
|
$contents[$key] = "define('PASSWORD', '" . hash('sha512', $_POST['password']) . "');\n";
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
file_put_contents(__FILE__, implode($contents));
|
|
|
|
echo 'Password changed successfully.';
|
|
}
|
|
break;
|
|
|
|
case 'delete':
|
|
if (isset($_POST['path']) && file_exists(MAIN_DIR . $_POST['path'])) {
|
|
$path = MAIN_DIR . $_POST['path'];
|
|
|
|
if ($_POST['path'] == '/') {
|
|
echo 'danger|Unable to delete main directory';
|
|
} else if (is_dir($path)) {
|
|
if (count(scandir($path)) !== 2) {
|
|
echo 'danger|Directory is not empty';
|
|
} else if (is_writable($path) === false) {
|
|
echo 'danger|Unable to delete directory';
|
|
} else {
|
|
if (in_array('deletedir', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
rmdir($path);
|
|
|
|
echo 'success|Directory deleted successfully';
|
|
}
|
|
} else {
|
|
file_to_history($path);
|
|
|
|
if (is_writable($path)) {
|
|
if (in_array('deletefile', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
unlink($path);
|
|
|
|
echo 'success|File deleted successfully';
|
|
} else {
|
|
echo 'danger|Unable to delete file';
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'rename':
|
|
if (isset($_POST['path']) && file_exists(MAIN_DIR . $_POST['path']) && isset($_POST['name']) && empty($_POST['name']) === false) {
|
|
$path = MAIN_DIR . $_POST['path'];
|
|
$new_path = str_replace(basename($path), '', dirname($path)) . DS . $_POST['name'];
|
|
|
|
if ($_POST['path'] == '/') {
|
|
echo 'danger|Unable to rename main directory';
|
|
} else if (is_dir($path)) {
|
|
if (in_array('renamedir', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
if (is_writable($path) === false) {
|
|
echo 'danger|Unable to rename directory';
|
|
} else {
|
|
rename($path, $new_path);
|
|
|
|
echo 'success|Directory renamed successfully';
|
|
}
|
|
} else {
|
|
if (in_array('renamefile', $permissions) !== true) {
|
|
die('danger|Permission denied');
|
|
}
|
|
|
|
file_to_history($path);
|
|
|
|
if (is_writable($path)) {
|
|
rename($path, $new_path);
|
|
|
|
echo 'success|File renamed successfully';
|
|
} else {
|
|
echo 'danger|Unable to rename file';
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'upload-file':
|
|
$files = $_FILES['uploadfile'];
|
|
|
|
if (is_array($files)) {
|
|
for ($i = 0; $i < count($files['name']); $i += 1) {
|
|
if (empty(PATTERN_FILES) === false && !preg_match(PATTERN_FILES, $files['name'][$i])) {
|
|
die('danger|Invalid file pattern: ' . htmlspecialchars($files['name'][$i]));
|
|
}
|
|
|
|
move_uploaded_file($files['tmp_name'][$i], MAIN_DIR . '/' . $files['name'][$i]);
|
|
}
|
|
}
|
|
|
|
echo 'success|File' . (count($files['name']) > 1 ? 's' : null) . ' uploaded successfully';
|
|
break;
|
|
}
|
|
|
|
exit;
|
|
}
|
|
|
|
function files($dir, $first = true)
|
|
{
|
|
$data = '';
|
|
|
|
if ($first === true) {
|
|
$data .= '<ul><li data-jstree=\'{ "opened" : true }\'><a href="#/" class="open-dir" data-dir="/">' . basename($dir) . '</a>';
|
|
}
|
|
|
|
$data .= '<ul class="files">';
|
|
$files = array_slice(scandir($dir), 2);
|
|
|
|
asort($files);
|
|
|
|
foreach ($files as $key => $file) {
|
|
if ((SHOW_PHP_SELF === false && $dir . DS . $file == __FILE__) || (SHOW_HIDDEN_FILES === false && substr($file, 0, 1) === '.')) {
|
|
continue;
|
|
}
|
|
|
|
if (is_dir($dir . DS . $file) && (empty(PATTERN_DIRECTORIES) || preg_match(PATTERN_DIRECTORIES, $file))) {
|
|
$dir_path = str_replace(MAIN_DIR . DS, '', $dir . DS . $file);
|
|
|
|
$data .= '<li class="dir"><a href="#/' . $dir_path . '/" class="open-dir" data-dir="/' . $dir_path . '/">' . $file . '</a>' . files($dir . DS . $file, false) . '</li>';
|
|
} else if (empty(PATTERN_FILES) || preg_match(PATTERN_FILES, $file)) {
|
|
$file_path = str_replace(MAIN_DIR . DS, '', $dir . DS . $file);
|
|
|
|
$data .= '<li class="file ' . (is_writable($file_path) ? 'editable' : null) . '" data-jstree=\'{ "icon" : "jstree-file" }\'><a href="#/' . $file_path . '" data-file="/' . $file_path . '" class="open-file">' . $file . '</a></li>';
|
|
}
|
|
}
|
|
|
|
$data .= '</ul>';
|
|
|
|
if ($first === true) {
|
|
$data .= '</li></ul>';
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
function redirect($address = null)
|
|
{
|
|
if (empty($address)) {
|
|
$address = $_SERVER['PHP_SELF'];
|
|
}
|
|
|
|
header('Location: ' . $address);
|
|
exit;
|
|
}
|
|
|
|
function file_to_history($file)
|
|
{
|
|
if (is_numeric(MAX_HISTORY_FILES) && MAX_HISTORY_FILES > 0) {
|
|
$file_dir = dirname($file);
|
|
$file_name = basename($file);
|
|
$file_history_dir = HISTORY_PATH . str_replace(MAIN_DIR, '', $file_dir);
|
|
|
|
foreach ([HISTORY_PATH, $file_history_dir] as $dir) {
|
|
if (file_exists($dir) === false || is_dir($dir) === false) {
|
|
mkdir($dir, 0777, true);
|
|
}
|
|
}
|
|
|
|
$history_files = scandir($file_history_dir);
|
|
|
|
foreach ($history_files as $key => $history_file) {
|
|
if (in_array($history_file, ['.', '..', '.DS_Store'])) {
|
|
unset($history_files[$key]);
|
|
}
|
|
}
|
|
|
|
$history_files = array_values($history_files);
|
|
|
|
if (count($history_files) >= MAX_HISTORY_FILES) {
|
|
foreach ($history_files as $key => $history_file) {
|
|
if ($key < 1) {
|
|
unlink($file_history_dir . DS . $history_file);
|
|
unset($history_files[$key]);
|
|
} else {
|
|
rename($file_history_dir . DS . $history_file, $file_history_dir . DS . $file_name . '.' . ($key - 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
copy($file, $file_history_dir . DS . $file_name . '.' . count($history_files));
|
|
}
|
|
}
|
|
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Pheditor</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default/style.min.css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/codemirror.min.css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/addon/lint/lint.min.css">
|
|
<style type="text/css">
|
|
h1,
|
|
h1 a,
|
|
h1 a:hover {
|
|
margin: 0;
|
|
padding: 0;
|
|
color: #444;
|
|
cursor: default;
|
|
text-decoration: none;
|
|
}
|
|
|
|
#files {
|
|
padding: 20px 10px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
#files>div {
|
|
overflow: auto;
|
|
}
|
|
|
|
#path {
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.dropdown-item.close {
|
|
font-size: 1em !important;
|
|
font-weight: normal;
|
|
opacity: 1;
|
|
}
|
|
|
|
.alert {
|
|
display: none;
|
|
position: fixed;
|
|
top: 10px;
|
|
right: 10px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
#loading {
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 9;
|
|
display: none;
|
|
position: absolute;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.lds-ring {
|
|
margin: 0 auto;
|
|
position: relative;
|
|
width: 64px;
|
|
height: 64px;
|
|
top: 45%;
|
|
}
|
|
|
|
.lds-ring div {
|
|
box-sizing: border-box;
|
|
display: block;
|
|
position: absolute;
|
|
width: 51px;
|
|
height: 51px;
|
|
margin: 6px;
|
|
border: 6px solid #fff;
|
|
border-radius: 50%;
|
|
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
|
border-color: #fff transparent transparent transparent;
|
|
}
|
|
|
|
.lds-ring div:nth-child(1) {
|
|
animation-delay: -0.45s;
|
|
}
|
|
|
|
.lds-ring div:nth-child(2) {
|
|
animation-delay: -0.3s;
|
|
}
|
|
|
|
.lds-ring div:nth-child(3) {
|
|
animation-delay: -0.15s;
|
|
}
|
|
|
|
@keyframes lds-ring {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
.dropdown-menu {
|
|
min-width: 12rem;
|
|
}
|
|
</style>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/jstree.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/codemirror.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/mode/javascript/javascript.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/mode/css/css.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/mode/php/php.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/mode/xml/xml.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/mode/htmlmixed/htmlmixed.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/mode/markdown/markdown.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/mode/clike/clike.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jshint/2.10.2/jshint.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsonlint/1.6.0/jsonlint.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/addon/lint/lint.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/addon/lint/javascript-lint.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/addon/lint/json-lint.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0/addon/lint/css-lint.min.js"></script>
|
|
<script type="text/javascript">
|
|
var editor,
|
|
modes = {
|
|
"js": "javascript",
|
|
"json": "javascript",
|
|
"md": "text/x-markdown"
|
|
},
|
|
last_keyup_press = false,
|
|
last_keyup_double = false;
|
|
|
|
function alertBox(message, className) {
|
|
$(".alert").removeClass("alert-success alert-warning alert-danger");
|
|
|
|
$(".alert").html(message).addClass("alert-" + className).fadeIn();
|
|
|
|
setTimeout(function() {
|
|
$(".alert").fadeOut();
|
|
}, 5000);
|
|
}
|
|
|
|
function reloadFiles(hash) {
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "reload"
|
|
}, function(data) {
|
|
$("#files > div").jstree("destroy");
|
|
$("#files > div").html(data);
|
|
$("#files > div").jstree();
|
|
$("#files > div a:first").click();
|
|
$("#path").html("");
|
|
|
|
window.location.hash = hash || "/";
|
|
|
|
if (hash) {
|
|
$("#files a[data-file=\"" + hash + "\"], #files a[data-dir=\"" + hash + "\"]").click();
|
|
}
|
|
});
|
|
}
|
|
|
|
function sha512(string) {
|
|
return crypto.subtle.digest("SHA-512", new TextEncoder("UTF-8").encode(string)).then(buffer => {
|
|
return Array.prototype.map.call(new Uint8Array(buffer), x => (("00" + x.toString(16)).slice(-2))).join("");
|
|
});
|
|
}
|
|
|
|
$(function() {
|
|
editor = CodeMirror.fromTextArea($("#editor")[0], {
|
|
lineNumbers: true,
|
|
mode: "application/x-httpd-php",
|
|
indentUnit: 4,
|
|
indentWithTabs: true,
|
|
lineWrapping: true,
|
|
gutters: ["CodeMirror-lint-markers"],
|
|
lint: true
|
|
});
|
|
|
|
$("#files > div").jstree({
|
|
state: {
|
|
key: "pheditor"
|
|
},
|
|
plugins: ["state"]
|
|
});
|
|
|
|
$("#files").on("dblclick", "a[data-file]", function(event) {
|
|
event.preventDefault();
|
|
<?php
|
|
|
|
$base_dir = str_replace($_SERVER['DOCUMENT_ROOT'], '', str_replace(DS, '/', MAIN_DIR));
|
|
|
|
if (substr($base_dir, 0, 1) !== '/') {
|
|
$base_dir = '/' . $base_dir;
|
|
}
|
|
|
|
?>
|
|
window.open("<?= $base_dir ?>" + $(this).attr("data-file"));
|
|
});
|
|
|
|
$("a.change-password").click(function() {
|
|
var password = prompt("Please enter new password:");
|
|
|
|
if (password != null && password.length > 0) {
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "password",
|
|
password: password
|
|
}, function(data) {
|
|
alert(data);
|
|
});
|
|
}
|
|
});
|
|
|
|
$(".dropdown .new-file").click(function() {
|
|
var path = $("#path").html();
|
|
|
|
if (path.length > 0) {
|
|
var name = prompt("Please enter file name:", "new-file"),
|
|
end = path.substring(path.length - 1),
|
|
file = "";
|
|
|
|
if (name != null && name.length > 0) {
|
|
if (end == "/") {
|
|
file = path + name;
|
|
} else {
|
|
file = path.substring(0, path.lastIndexOf("/") + 1) + name;
|
|
}
|
|
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "save",
|
|
file: file,
|
|
data: ""
|
|
}, function(data) {
|
|
data = data.split("|");
|
|
|
|
alertBox(data[1], data[0]);
|
|
|
|
if (data[0] == "success") {
|
|
reloadFiles();
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
alertBox("Please select a file or directory", "warning");
|
|
}
|
|
});
|
|
|
|
$(".dropdown .new-dir").click(function() {
|
|
var path = $("#path").html();
|
|
|
|
if (path.length > 0) {
|
|
var name = prompt("Please enter directory name:", "new-dir"),
|
|
end = path.substring(path.length - 1),
|
|
dir = "";
|
|
|
|
if (name != null && name.length > 0) {
|
|
if (end == "/") {
|
|
dir = path + name;
|
|
} else {
|
|
dir = path.substring(0, path.lastIndexOf("/") + 1) + name;
|
|
}
|
|
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "make-dir",
|
|
dir: dir
|
|
}, function(data) {
|
|
data = data.split("|");
|
|
|
|
alertBox(data[1], data[0]);
|
|
|
|
if (data[0] == "success") {
|
|
reloadFiles();
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
alertBox("Please select a file or directory", "warning");
|
|
}
|
|
});
|
|
|
|
$(".dropdown .save").click(function() {
|
|
var path = $("#path").html(),
|
|
data = editor.getValue();
|
|
|
|
if (path.length > 0) {
|
|
sha512(data).then(function(digest) {
|
|
$("#digest").val(digest);
|
|
});
|
|
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "save",
|
|
file: path,
|
|
data: data
|
|
}, function(data) {
|
|
data = data.split("|");
|
|
|
|
alertBox(data[1], data[0]);
|
|
});
|
|
} else {
|
|
alertBox("Please select a file", "warning");
|
|
}
|
|
});
|
|
|
|
$(".dropdown .close").click(function() {
|
|
editor.setValue("");
|
|
$("#files > div a:first").click();
|
|
$(".dropdown").find(".save, .delete, .rename, .reopen, .close").addClass("disabled");
|
|
});
|
|
|
|
$(".dropdown .delete").click(function() {
|
|
var path = $("#path").html();
|
|
|
|
if (path.length > 0) {
|
|
if (confirm("Are you sure to delete this file?")) {
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "delete",
|
|
path: path
|
|
}, function(data) {
|
|
data = data.split("|");
|
|
|
|
alertBox(data[1], data[0]);
|
|
|
|
if (data[0] == "success") {
|
|
reloadFiles();
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
alertBox("Please select a file or directory", "warning");
|
|
}
|
|
});
|
|
|
|
$(".dropdown .rename").click(function() {
|
|
var path = $("#path").html(),
|
|
split = path.split("/"),
|
|
file = split[split.length - 1],
|
|
dir = split[split.length - 2],
|
|
new_file_name;
|
|
|
|
if (path.length > 0) {
|
|
if (file.length > 0) {
|
|
new_file_name = file;
|
|
} else if (dir.length > 0) {
|
|
new_file_name = dir;
|
|
} else {
|
|
new_file_name = "new-file";
|
|
}
|
|
|
|
var name = prompt("Please enter new name:", new_file_name);
|
|
|
|
if (name != null && name.length > 0) {
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "rename",
|
|
path: path,
|
|
name: name
|
|
}, function(data) {
|
|
data = data.split("|");
|
|
|
|
alertBox(data[1], data[0]);
|
|
|
|
if (data[0] == "success") {
|
|
reloadFiles(path.substring(0, path.lastIndexOf("/")) + "/" + name);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
alertBox("Please select a file or directory", "warning");
|
|
}
|
|
});
|
|
|
|
$(".dropdown .reopen").click(function() {
|
|
var path = $("#path").html();
|
|
|
|
if (path.length > 0) {
|
|
$(window).trigger("hashchange");
|
|
}
|
|
});
|
|
|
|
$(window).resize(function() {
|
|
if (window.innerWidth >= 720) {
|
|
var height = window.innerHeight - $(".CodeMirror")[0].getBoundingClientRect().top - 20;
|
|
|
|
$("#files, .CodeMirror").css("height", height + "px");
|
|
} else {
|
|
$("#files > div, .CodeMirror").css("height", "");
|
|
}
|
|
});
|
|
|
|
$(window).resize();
|
|
|
|
$(".alert").click(function() {
|
|
$(this).fadeOut();
|
|
});
|
|
|
|
$(document).bind("keyup keydown", function(event) {
|
|
if ((event.ctrlKey || event.metaKey) && event.shiftKey) {
|
|
if (event.keyCode == 78) {
|
|
$(".dropdown .new-file").click();
|
|
event.preventDefault();
|
|
|
|
return false;
|
|
} else if (event.keyCode == 83) {
|
|
$(".dropdown .save").click();
|
|
event.preventDefault();
|
|
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
|
|
$(document).bind("keyup", function(event) {
|
|
if (event.keyCode == 27) {
|
|
if (last_keyup_press == true) {
|
|
last_keyup_double = true;
|
|
|
|
$("#fileMenu").click();
|
|
$("body").focus();
|
|
} else {
|
|
last_keyup_press = true;
|
|
|
|
setTimeout(function() {
|
|
if (last_keyup_double === false) {
|
|
if (document.activeElement.tagName.toLowerCase() == "textarea") {
|
|
$(".jstree-clicked").focus();
|
|
} else {
|
|
editor.focus();
|
|
}
|
|
}
|
|
|
|
last_keyup_press = false;
|
|
last_keyup_double = false;
|
|
}, 250);
|
|
}
|
|
}
|
|
});
|
|
|
|
$(window).on("hashchange", function() {
|
|
var hash = window.location.hash.substring(1),
|
|
data = editor.getValue();
|
|
|
|
if (hash.length > 0) {
|
|
sha512(data).then(function(digest) {
|
|
if ($("#digest").val().length < 1 || $("#digest").val() == digest) {
|
|
if (hash.substring(hash.length - 1) == "/") {
|
|
var dir = $("a[data-dir='" + hash + "']");
|
|
|
|
if (dir.length > 0) {
|
|
editor.setValue("");
|
|
$("#digest").val("");
|
|
$("#path").html(hash);
|
|
$(".dropdown").find(".save, .reopen, .close").addClass("disabled");
|
|
$(".dropdown").find(".delete, .rename").removeClass("disabled");
|
|
}
|
|
} else {
|
|
var file = $("a[data-file='" + hash + "']");
|
|
|
|
if (file.length > 0) {
|
|
$("#loading").fadeIn(250);
|
|
|
|
$.post("<?= $_SERVER['PHP_SELF'] ?>", {
|
|
action: "open",
|
|
file: encodeURIComponent(hash)
|
|
}, function(data) {
|
|
editor.setValue(data);
|
|
editor.setOption("mode", "application/x-httpd-php");
|
|
|
|
sha512(data).then(function(digest) {
|
|
$("#digest").val(digest);
|
|
});
|
|
|
|
if (hash.lastIndexOf(".") > 0) {
|
|
var extension = hash.substring(hash.lastIndexOf(".") + 1);
|
|
|
|
if (modes[extension]) {
|
|
editor.setOption("mode", modes[extension]);
|
|
}
|
|
}
|
|
|
|
$("#editor").attr("data-file", hash);
|
|
$("#path").html(hash).hide().fadeIn(250);
|
|
$(".dropdown").find(".save, .delete, .rename, .reopen, .close").removeClass("disabled");
|
|
|
|
$("#loading").fadeOut(250);
|
|
});
|
|
}
|
|
}
|
|
} else if (confirm("Discard changes?")) {
|
|
$("#digest").val("");
|
|
|
|
$(window).trigger("hashchange");
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
if (window.location.hash.length < 1) {
|
|
window.location.hash = "/";
|
|
} else {
|
|
$(window).trigger("hashchange");
|
|
}
|
|
|
|
$("#files").on("click", ".jstree-anchor", function() {
|
|
location.href = $(this).attr("href");
|
|
});
|
|
|
|
$(document).ajaxError(function(event, request, settings) {
|
|
var message = "An error occurred with this request.";
|
|
|
|
if (request.responseText.length > 0) {
|
|
message = request.responseText;
|
|
}
|
|
|
|
if (confirm(message + " Do you want to reload the page?")) {
|
|
location.reload();
|
|
}
|
|
|
|
$("#loading").fadeOut(250);
|
|
});
|
|
|
|
$(window).keydown(function(event) {
|
|
if ($("#fileMenu[aria-expanded='true']").length > 0) {
|
|
var code = event.keyCode;
|
|
|
|
if (code == 78) {
|
|
$(".new-file").click();
|
|
} else if (code == 83) {
|
|
$(".save").click();
|
|
} else if (code == 68) {
|
|
$(".delete").click();
|
|
} else if (code == 82) {
|
|
$(".rename").click();
|
|
} else if (code == 79) {
|
|
$(".reopen").click();
|
|
} else if (code == 67) {
|
|
$(".close").click();
|
|
} else if (code == 85) {
|
|
$(".upload-file").click();
|
|
}
|
|
}
|
|
});
|
|
|
|
$(".dropdown .upload-file").click(function() {
|
|
$("#uploadFileModal").modal("show");
|
|
$("#uploadFileModal input").focus();
|
|
});
|
|
|
|
$("#uploadFileModal button").click(function() {
|
|
// var input = $("#uploadFileModal input").val();
|
|
var form = $(this).closest("form"),
|
|
formdata = false;
|
|
|
|
if (window.FormData) {
|
|
formdata = new FormData(form[0]);
|
|
}
|
|
|
|
$.ajax({
|
|
url: "<?= $_SERVER['PHP_SELF'] ?>",
|
|
data: formdata ? formdata : form.serialize(),
|
|
cache: false,
|
|
contentType: false,
|
|
processData: false,
|
|
type: "POST",
|
|
success: function(data, textStatus, jqXHR) {
|
|
data = data.split("|");
|
|
|
|
alertBox(data[1], data[0]);
|
|
|
|
if (data[0] == "success") {
|
|
reloadFiles();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div class="container-fluid">
|
|
|
|
<div class="row p-3">
|
|
<div class="col-md-3">
|
|
<h1><a href="http://github.com/hamidsamak/pheditor" target="_blank" title="Pheditor <?= VERSION ?>">Pheditor</a></h1>
|
|
</div>
|
|
<div class="col-md-9">
|
|
<div class="float-left">
|
|
<div class="dropdown float-left">
|
|
<button class="btn btn-secondary dropdown-toggle" type="button" id="fileMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">File</button>
|
|
<div class="dropdown-menu" aria-labelledby="fileMenu">
|
|
<?php if (in_array('newfile', $permissions)) { ?>
|
|
<a class="dropdown-item new-file" href="javascript:void(0);">New File <span class="float-right text-secondary">N</span></a>
|
|
<?php } ?>
|
|
|
|
<?php if (in_array('newdir', $permissions)) { ?>
|
|
<a class="dropdown-item new-dir" href="javascript:void(0);">New Directory</a>
|
|
<?php } ?>
|
|
|
|
<?php if (in_array('uploadfile', $permissions)) { ?>
|
|
<a class="dropdown-item upload-file" href="javascript:void(0);">Upload File <span class="float-right text-secondary">U</span></a>
|
|
<?php } ?>
|
|
|
|
<?php if (in_array('newfile', $permissions) || in_array('newdir', $permissions)) { ?>
|
|
<div class="dropdown-divider"></div>
|
|
<?php } ?>
|
|
|
|
<?php if (in_array('newfile', $permissions) || in_array('editfile', $permissions)) { ?>
|
|
<a class="dropdown-item save disabled" href="javascript:void(0);">Save <span class="float-right text-secondary">S</span></a>
|
|
<?php } ?>
|
|
|
|
<?php if (in_array('deletefile', $permissions) || in_array('deletedir', $permissions)) { ?>
|
|
<a class="dropdown-item delete disabled" href="javascript:void(0);">Delete <span class="float-right text-secondary">D</span></a>
|
|
<?php } ?>
|
|
|
|
<?php if (in_array('renamefile', $permissions) || in_array('renamedir', $permissions)) { ?>
|
|
<a class="dropdown-item rename disabled" href="javascript:void(0);">Rename <span class="float-right text-secondary">R</span></a>
|
|
<?php } ?>
|
|
|
|
<a class="dropdown-item reopen disabled" href="javascript:void(0);">Re-open <span class="float-right text-secondary">O</span></a>
|
|
<div class="dropdown-divider"></div>
|
|
<a class="dropdown-item close disabled" href="javascript:void(0);">Close <span class="float-right text-secondary">C</span></a>
|
|
</div>
|
|
</div>
|
|
<span id="path" class="btn float-left"></span>
|
|
</div>
|
|
|
|
<div class="float-right">
|
|
<?php if (in_array('changepassword', $permissions)) { ?><a href="javascript:void(0);" class="change-password btn btn-sm btn-primary">Password</a> <?php } ?><a href="<?= $_SERVER['PHP_SELF'] ?>?logout=1" class="btn btn-sm btn-danger">Logout</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row px-3">
|
|
<div class="col-lg-3 col-md-3 col-sm-12 col-12">
|
|
<div id="files" class="card">
|
|
<div class="card-block"><?= files(MAIN_DIR) ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-9 col-md-9 col-sm-12 col-12">
|
|
<div class="card">
|
|
<div class="card-block">
|
|
<div id="loading">
|
|
<div class="lds-ring">
|
|
<div></div>
|
|
<div></div>
|
|
<div></div>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
<textarea id="editor" data-file="" class="form-control"></textarea>
|
|
<input id="digest" type="hidden" readonly>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="alert"></div>
|
|
|
|
<form method="post">
|
|
<input name="action" type="hidden" value="upload-file">
|
|
|
|
<div class="modal" id="uploadFileModal">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Upload File</h4>
|
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div>
|
|
<input name="uploadfile[]" type="file" value="" multiple>
|
|
</div>
|
|
<?php
|
|
|
|
if (function_exists('ini_get')) {
|
|
$sizes = [
|
|
ini_get('post_max_size'),
|
|
ini_get('upload_max_filesize')
|
|
];
|
|
|
|
$max_size = max($sizes);
|
|
|
|
echo '<small class="text-muted">Maximum file size: ' . $max_size . '</small>';
|
|
}
|
|
|
|
?>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-success" data-dismiss="modal">Upload</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
</body>
|
|
|
|
</html>
|