mirror of
https://github.com/netfun2000/pheditor.git
synced 2026-02-27 09:45:14 +08:00
29 lines
18 KiB
PHP
29 lines
18 KiB
PHP
<?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('EDITABLE_FORMATS','txt,php,htm,html,js,css,tpl,xml,md,json');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);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(1440,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));}}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();}if(isset($_POST['action'])){if(isset($_POST['file'])&&empty($_POST['file'])===false){$formats=explode(',',EDITABLE_FORMATS);if(($position=strrpos($_POST['file'],'.'))!==false){$extension=substr($_POST['file'],$position+1);}else{$extension=null;}if(empty(EDITABLE_FORMATS)===false&&empty($extension)===false&&in_array(strtolower($extension),$formats)!==true){die('INVALID_EDITABLE_FORMAT');}if(strpos($_POST['file'],'../')!==false||strpos($_POST['file'],'..\'')!==false){die('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){file_put_contents($file,$_POST['data']);echo 'success|File saved successfully';}elseif(is_writable($file)===false){echo 'danger|File is not writable';}else{if(file_exists($_POST['file'])){file_to_history($file);}file_put_contents($file,$_POST['data']);echo 'success|File saved successfully';}}break;case 'make-dir':$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(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';}elseif(is_dir($path)){if(count(scandir($path))!==2){echo 'danger|Directory is not empty';}elseif(is_writable($path)===false){echo 'danger|Unable to delete directory';}else{rmdir($path);echo 'success|Directory deleted successfully';}}else{file_to_history($path);if(is_writable($path)){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';}elseif(is_dir($path)){if(is_writable($path)===false){echo 'danger|Unable to rename directory';}else{rename($path,$new_path);echo 'success|Directory renamed successfully';}}else{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;}exit;}function files($dir,$first=true){$data='';if($first===true){$data.='<ul><li data-jstree=\'{ "opened" : true }\'><a href="javascript:void(0);" class="open-dir" data-dir="/">'.basename($dir).'</a>';}$formats=empty(EDITABLE_FORMATS)?[]:explode(',',EDITABLE_FORMATS);$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)){$dir_path=str_replace(MAIN_DIR.DS,'',$dir.DS.$file);$data.='<li class="dir"><a href="javascript:void(0);" class="open-dir" data-dir="/'.$dir_path.'/">'.$file.'</a>'.files($dir.DS.$file,false).'</li>';}else{$file_path=str_replace(MAIN_DIR.DS,'',$dir.DS.$file);$is_editable=count($formats)<1||strpos($file,'.')===false||in_array(substr($file,strrpos($file,'.')+1),$formats);$data.='<li class="file '.($is_editable?'editable':null).'" data-jstree=\'{ "icon" : "jstree-file" }\'><a href="javascript:void(0);" data-file="/'.$file_path.'"'.($is_editable?' class="open-file"':null).'>'.$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.DS.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);}}$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" /><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;
|
|
}</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 type="text/javascript">var editor, modes = { "js": "javascript", "json": "javascript", "md": "text/x-markdown" };
|
|
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() { $.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 = "/"; });
|
|
}
|
|
$(function(){ editor = CodeMirror.fromTextArea($("#editor")[0], { lineNumbers: true, mode: "application/x-httpd-php", indentUnit: 4, indentWithTabs: true, lineWrapping: true }); $("#files > div").jstree({ state: { key: "pheditor" }, plugins: [ "state" ] }); $("#files").on("click", "a.open-file", function(event){ event.preventDefault(); var file = $(this).attr("data-file"), _this = $(this); window.location.hash = file; $.post("<?=$_SERVER['PHP_SELF'] ?>", { action: "open", file: encodeURIComponent(file) }, function(data){ editor.setValue(data); editor.setOption("mode", "application/x-httpd-php"); if (file.lastIndexOf(".") > 0) { var extension = file.substring(file.lastIndexOf(".") + 1); if (modes[extension]) { editor.setOption("mode", modes[extension]); } } $("#editor").attr("data-file", file); $("#path").html(file); $(".dropdown").find(".save, .delete, .rename, .reopen, .close").removeClass("disabled"); }); }); $("#files").on("dblclick", "a[data-file]", function(event){ event.preventDefault(); window.open("<?=str_replace($_SERVER['DOCUMENT_ROOT'],'',str_replace(DS,'/',MAIN_DIR)) ?>" + $(this).attr("data-file")); }); $("#files").on("click", "a.open-dir", function(event){ event.preventDefault(); var dir = $(this).attr("data-dir"), _this = $(this); window.location.hash = dir; editor.setValue(""); $("#path").html(dir); $(".dropdown").find(".save, .reopen, .close").addClass("disabled"); $(".dropdown").find(".delete, .rename").removeClass("disabled"); }); if (window.location.hash.length > 1) { var hash = window.location.hash.substring(1); setTimeout(function(){ $("#files a[data-file=\"" + hash + "\"], #files a[data-dir=\"" + hash + "\"]").click(); }, 500); } $("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) { $.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(); if (path.length > 0) { var name = prompt("Please enter new name:", "new-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(); } }); } } else { alertBox("Please select a file or directory", "warning"); } }); $(".dropdown .reopen").click(function(){ var path = $("#path").html(); if (path.length > 0) { $("#files a[data-file=\"" + path + "\"], #files a[data-dir=\"" + path + "\"]").click(); } }); $(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 (document.activeElement.tagName.toLowerCase() == "textarea") { $(".jstree-clicked").focus(); } else { editor.focus(); } } });
|
|
});</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"> <a class="dropdown-item new-file" href="javascript:void(0);">New File</a> <a class="dropdown-item new-dir" href="javascript:void(0);">New Directory</a> <div class="dropdown-divider"></div> <a class="dropdown-item save disabled" href="javascript:void(0);">Save</a> <a class="dropdown-item delete disabled" href="javascript:void(0);">Delete</a> <a class="dropdown-item rename disabled" href="javascript:void(0);">Rename</a> <a class="dropdown-item reopen disabled" href="javascript:void(0);">Re-open</a> <div class="dropdown-divider"></div> <a class="dropdown-item close disabled" href="javascript:void(0);">Close</a> </div> </div> <span id="path" class="btn float-left"></span> </div> <div class="float-right"> <a href="javascript:void(0);" class="change-password btn btn-sm btn-primary">Password</a> <a href="<?=$_SERVER['PHP_SELF'] ?>?logout=1" class="btn btn-sm btn-danger">Logout</a> </div> </div> </div> <div class="row p-3"> <div class="col-lg-3 col-md-3 col-sm-12 col-xs-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-xs-12"> <div class="card"> <div class="card-block"> <textarea id="editor" data-file="" class="form-control"></textarea> </div> </div> </div> </div></div><div class="alert"></div></body></html>
|