├── .gitignore
├── serve.sh
├── src
├── shell-motd.txt
├── login.html
├── set-password.php
├── util.php
├── commands.php
├── editor.css
├── index.php
└── editor.js
├── package.json
├── LICENSE
├── README.md
├── Gruntfile.js
└── dist
└── editor.php
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/serve.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | php -S localhost:8080 -t src/
4 |
--------------------------------------------------------------------------------
/src/shell-motd.txt:
--------------------------------------------------------------------------------
1 | Common shell commands:
2 | du -hd1|sort -h
3 | fdupes -rq .
4 | ps -eo pcpu,time,pid,args|tail -n +2|sort -nrk1
5 | iotop -obn1
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "editor",
3 | "version": "1.0.1-master",
4 | "author": "Simon Thorpe",
5 | "description": "A quick and dirty PHP file manager",
6 | "engines": {
7 | "node": ">= 0.10.0"
8 | },
9 | "devDependencies": {
10 | "grunt": "~0.4.2",
11 | "grunt-contrib-clean": "~1.0.0",
12 | "grunt-prettify": "~0.4.0",
13 | "grunt-esformatter": "~1.1.0",
14 | "grunt-contrib-jshint": "~1.0.0",
15 | "grunt-contrib-concat": "~1.0.0",
16 | "grunt-contrib-uglify": "~0.11.1",
17 | "grunt-contrib-cssmin": "~0.14.0",
18 | "grunt-replace": "~0.11.0",
19 | "grunt-shell": "~1.2.1",
20 | "grunt-jscs": "~2.7.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Simon Thorpe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Code Editor - Login
6 |
7 |
17 |
18 |
35 |
36 |
37 |
45 |
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project is a single PHP file that can be uploaded to a webserver. It will then allow browsing and editing of files using an HTML5 editor (no browser plugins required).
2 |
3 | The purpose of this project is to allow code editing/deleting/uploading on shared web space where FTP or other means of editing is unavailable or not practical (such as a Chromebook).
4 |
5 | #### Advantages over FTP
6 | * No IDE software needs to be installed
7 | * No complex firewall rules
8 | * HTML5 only - perfect for Chrome OS
9 |
10 | #### Disadvantages over FTP
11 | * Limited functionality
12 | * Must upload php script to begin with
13 |
14 | ## Installation
15 | Upload the editor.php file (in the dist/ directory) to the root of your web space then point your browser to http://yoursite.com/editor.php and login to edit files. Default password is 'admin'.
16 |
17 | #### Install - via SSH
18 | ```
19 | cd public_html
20 | wget https://raw.githubusercontent.com/simon-thorpe/editor/master/dist/editor.php
21 | ```
22 |
23 | #### Install - via FTP
24 | * Upload editor.php to the root of your web directory
25 |
26 | #### Install - via CMS
27 | Some content management systems allow creating simple text files. All you need to do is create a file editor.php (or any name you like) then paste the contents of dist/editor.php into that file.
28 |
29 | ## Building from Source
30 | ```
31 | git clone https://github.com/simon-thorpe/editor.git
32 | cd editor
33 | npm install
34 | # edit dev files in src dir
35 | grunt
36 | # built file is in dist dir
37 | ```
38 |
39 | ## API
40 | Uploading a file to the server
41 | ```
42 | curl yoursite.com/editor.php -s -H "Cookie: editor-auth=yourpass" -F p=/home/yoursite/public_html/calendar.json -F 'content=
2 |
3 |
4 |
5 | Code Editor - Set Password
6 |
7 | ",FILE_APPEND);
10 | header('Location: '.$_SERVER['SCRIPT_NAME']);
11 | }
12 | ?>
13 |
14 |
15 |
25 |
26 |
35 |
36 |
37 |
55 |
56 |
--------------------------------------------------------------------------------
/src/util.php:
--------------------------------------------------------------------------------
1 | 0 && $dir!='/')$relativePath=substr($fullPath,strlen($dir)+1);
9 | else $relativePath=$fullPath;
10 |
11 | // Remove trailing '/' as output should be relative to $dir.
12 | if($relativePath[0]=='/')
13 | $relativePath=substr($relativePath,1);
14 |
15 | array_push($r,$relativePath);
16 | }
17 | return $r;
18 | }
19 | function scandir_recursive($dir,$includeDirs=TRUE,$_prefix=''){
20 | $dir=rtrim($dir,'\\/');
21 | $r=array();
22 | foreach(scandir($dir)as$f){
23 | if($f!=='.'&&$f!=='..')
24 | if(is_dir("$dir/$f")){
25 | $r=array_merge($r,scandir_recursive("$dir/$f",$includeDirs,"$_prefix$f/"));
26 | if($includeDirs)$r[]=$_prefix.$f;
27 | }else $r[]=$_prefix.$f;
28 | }
29 | return $r;
30 | }
31 | function human_readable_filesize($v)
32 | {
33 | if($v>=1024*1024*1024)
34 | return (floor($v/(1024*1024*1024)*10)/10)." GB";
35 | elseif($v>=1024*1024)
36 | return (floor($v/(1024*1024)*10)/10)." MB";
37 | elseif($v>=1024)
38 | return (floor($v/1024*10)/10)." KB";
39 | else
40 | return $v." bytes";
41 | }
42 | function human_readable_timespan($v)
43 | {
44 | if($v>=60*60*24)
45 | return (floor($v/(60*60*24)*10)/10)." days";
46 | elseif($v>=60*60)
47 | return (floor($v/(60*60)*10)/10)." hours";
48 | elseif($v>=60)
49 | return floor($v/60)." min";
50 | else
51 | return $v." sec";
52 | }
53 | function get_dir_count($dir,$du=false){
54 | if(is_readable($dir)){
55 | if($du==TRUE){ // Recursively calculate the size of the directory's descendant items.
56 | $output=shell_exec('/usr/bin/find '.escapeshellarg($dir).' |wc -l');
57 | return intval($output)-1;
58 | }
59 | else{
60 | // Cound the items within the directory.
61 | return count(scandir($dir))-2; // -2 to exclude the "." and ".."
62 | }
63 | }
64 | else
65 | return null;
66 | }
67 | function urlencodelite($s){
68 | $s=rawurlencode($s);
69 | $s=str_replace('%2F','/',$s);
70 | $s=str_replace(' ','%20',$s);
71 | return $s;
72 | }
73 | ?>
--------------------------------------------------------------------------------
/src/commands.php:
--------------------------------------------------------------------------------
1 | false,"message"=>"File already exists."));
39 | else{
40 | if($type==='dir'){
41 | $r=mkdir($PATH);
42 | }
43 | elseif($type==='file'){
44 | $r=file_put_contents($PATH,'');
45 | }
46 | //echo '{"success":'.($r!==false?'true':'false').'}';
47 | echo json_encode(array("success"=>$r!==false,"message"=>"Error writing ".$PATH));
48 | }
49 | exit;
50 | }
51 | elseif(isset($_GET["d"])){
52 | // http://php.net/manual/en/function.readfile.php
53 | header('Content-Description: File Transfer');
54 | $fi=finfo_open(FILEINFO_MIME);
55 | header('Content-Type: '.finfo_file($fi,$PATH));
56 | finfo_close($fi);
57 | header('Content-Disposition: inline; filename='.basename($PATH));
58 | header('Content-Transfer-Encoding: binary');
59 | header('Cache-Control: private, max-age=0, must-revalidate');
60 | header('Content-Length: '.filesize($PATH));
61 | ob_clean();
62 | flush();
63 | readfile($PATH);
64 | exit;
65 | }
66 | elseif(isset($_FILES['file'])){
67 | $dest=$PATH.'/'.$_FILES['file']['name'];
68 | $tempFile=$_FILES['file']['tmp_name'];
69 | header('Content-Type: application/json');
70 | echo json_encode(array("success"=>move_uploaded_file($_FILES['file']['tmp_name'],$dest)===true));
71 | exit;
72 | }
73 | ?>
--------------------------------------------------------------------------------
/src/editor.css:
--------------------------------------------------------------------------------
1 | body{font-family:sans-serif;font-size:11px;margin:0;}
2 | a{text-decoration:none;}
3 | input[type="text"]{border:1px solid #444;}
4 |
5 | header nav a{float:left;padding:6px 7px;color:#444;}
6 | header nav a:hover{background-color:#aaa;}
7 | header nav:after{content:"";clear:both;display:table;}
8 | header nav .active{font-weight:bold;}
9 |
10 | form{display:none;border-top:1px solid #444;padding:10px;}
11 | form .fieldRow{margin-bottom:5px;}
12 | pre{margin:0 0 10px;}
13 | input[readonly]{background-color:#ddd;opacity:0.3;}
14 |
15 | #list{border-bottom:1px solid #444;font-weight:bold;}
16 | #list>div{width:100%;border-top:1px solid #444;display:table;}
17 | #list>div>*{display:table-cell;padding:2px 0 2px 3px;}
18 | #list>div>a .indent{margin-left:10px;}
19 | #list>div.bad{text-decoration:line-through;}
20 | #list .cut{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff;background-image:url('//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/cut.png');}
21 | #list .copy{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff;background-image:url('//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/page_copy.png');}
22 | #list .paste{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff;background-image:url('//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/page_paste.png');}
23 | #list .dir .paste{display:none;}
24 | #list .remove-clip{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff;background-image:url('//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/delete.png');}
25 | #list .del{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff;background-image:url('//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/delete.png');}
26 | #list .del:not([onclick]){display:none;}
27 | #list .direct{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff;background-image:url('//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/world_go.png');}
28 | #list .direct[href=""]{display:none;}
29 | #list .dl{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff;background-image:url('//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/inbox_download.png');}
30 | #list>div:nth-of-type(odd){background-color:#dfd;}
31 | #list>div:hover{background-color:#ddd;}
32 | #list>div:nth-of-type(odd):hover{background-color:#ddd;}
33 | #list .seg:hover{text-decoration:underline;color:#444 !important;}
34 | #list .seg[href=""]:hover{text-decoration:none;color:#ccc !important;}
35 | #list .dir{cursor:pointer;}
36 | #list .file{cursor:pointer;}
37 | #list .filepath .slash{color:#bbb;}
38 | #list .dir .filepath,#list .dir .seg{color:#bb0;}
39 | #list .file .filepath,#list .file .seg{color:#1b0;}
40 | #list .file .seg.d{color:#bb0;}
41 | #list .seg[href=""]{color:#ccc;cursor:default;} /* File editing disabled - too large to edit */
42 | #list .size{color:#bbb;margin-left:5px;font-size:10px;width:100px;}
43 | #list .dir .size:after{content:" files";}
44 | #list .age:after{content:" old";}
45 | #list .age{color:#bbb;font-size:10px;width:100px;}
46 |
47 | /*@media (max-width:979px){
48 | body{font-size:12px;}
49 | #list>div>*{display:block;padding-bottom:0;}
50 | #list .size{float:left;height:16px;height:auto;padding:0 0 4px;}
51 | #list .age{float:left;height:16px;height:auto;padding:0 0 4px;}
52 | #list .direct,#list .dl,#list .del,#list .cut,#list .copy,#list .paste,#list .remove-clip{float:right;height:34px;width:34px;margin-top:-18px;padding:0;}
53 | #list .filepath{width:calc(100% - 132px);}
54 | }*/
55 |
56 | @media (max-width:979px){
57 | body{font-size:12px;}
58 | #list>div>*{display:block;padding-bottom:0;}
59 | #list .size{float:left;height:16px;height:auto;padding:0 0 4px;}
60 | #list .age{float:left;height:16px;height:auto;padding:0 0 4px;}
61 | #list .direct,#list .dl,#list .del,#list .cut,#list .copy,#list .paste,#list .remove-clip{float:right;height:17px;width:17px;padding:0;}
62 | }
63 |
64 | button{border:none;margin:0;font-size:10px;}
65 | #editor{margin-top:15px;position:absolute;top:0;bottom:0;left:0;right:0;}
66 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*global module:false*/
2 | module.exports = function(grunt) {
3 |
4 | var pkg=grunt.file.readJSON('package.json');
5 |
6 | // Project configuration.
7 | grunt.initConfig({
8 | // Metadata.
9 | pkg: pkg,
10 | trimPhpTags: function(s){s=s.substring(6);return s.substring(0,s.length-3);},
11 | // Task configuration.
12 | clean: {
13 | pre: ['build/','dist/','src/editor.config.php'],
14 | post: ['build/']
15 | },
16 | prettify: {
17 | options: {
18 | indent: 1,
19 | indent_char: ' ',
20 | unformatted: [
21 | "noscript"
22 | ]
23 | },
24 | all: {
25 | expand: true,
26 | cwd: 'src/',
27 | src: ['*.html', 'set-password.php'],
28 | dest: 'src/'
29 | }
30 | },
31 | jscs: {
32 | src: 'src/**/*.js',
33 | options: {
34 | "preset": "google",
35 | "maximumLineLength": 1000,
36 | "disallowMultipleVarDecl": false
37 | }
38 | },
39 | esformatter: {
40 | src: 'src/**/*.js'
41 | },
42 | concat: {
43 | options: {
44 | stripBanners: true
45 | },
46 | dist: {
47 | src: ['src/<%= pkg.name %>.js'],
48 | dest: 'build/<%= pkg.name %>.min.js'
49 | }
50 | },
51 | uglify: {
52 | options: {
53 | },
54 | dist: {
55 | src: '<%= concat.dist.dest %>',
56 | dest: 'build/<%= pkg.name %>.min.js'
57 | }
58 | },
59 | jshint: {
60 | options: {
61 | curly: true,
62 | eqeqeq: true,
63 | immed: true,
64 | latedef: true,
65 | newcap: true,
66 | noarg: true,
67 | sub: true,
68 | undef: true,
69 | unused: true,
70 | boss: true,
71 | eqnull: true,
72 | browser: true,
73 | devel: true, // supress alert() warnings
74 | globals: {
75 | jQuery: true, // supress jQuery undefined warnings
76 | ace: true
77 | }
78 | },
79 | gruntfile: {
80 | src: 'Gruntfile.js'
81 | },
82 | all: {
83 | src: ['src/**/*.js']
84 | }
85 | },
86 | cssmin: {
87 | minify: {
88 | expand: true,
89 | cwd: 'src/',
90 | src: ['*.css', '!*.min.css'],
91 | dest: 'build/',
92 | ext: '.min.css'
93 | }
94 | },
95 | replace: {
96 | dist: {
97 | options: {
98 | patterns: [
99 | // Use functions to return value for replacement instead of "replacement: '<%= grunt.file.read("src/shell-motd.txt") %>'" otherwise regex will clash with content inside the include target.
100 | {
101 | match: 'version',
102 | replacement: '<%= pkg.version %>'
103 | },
104 | {
105 | match: 'buildDate',
106 | replacement: new Date().toISOString()
107 | },
108 | {
109 | match: /require\('commands.php'\);/,
110 | replacement: function(){
111 | var s=grunt.file.read("src/commands.php");
112 | s=s.substring(6); // trim leading ""
114 | }
115 | },
116 | {
117 | match: /require\('set-password.php'\);/,
118 | replacement: function(){return '?>'+grunt.file.read("src/set-password.php")+''+grunt.file.read("src/login.html")+'/,
126 | replacement: function(){return grunt.file.read("src/shell-motd.txt");}
127 | },
128 | {
129 | match: /require\('util.php'\);/,
130 | replacement: function(){
131 | var s=grunt.file.read("src/util.php");
132 | s=s.substring(6); // trim leading ""
134 | }
135 | },
136 |
137 | { // replace external css ref
138 | match: / /,
139 | //replacement: function(){return '';}
140 | replacement: function(){return ' ';}
141 | },
142 | { // with php handler
143 | match: /\/\/ @@css/,
144 | replacement: function(){
145 | var r = "if(isset($_GET['css'])){";
146 | r += "header('Content-Type: text/css');\n";
147 | r += "header('Cache-Control: public, maxage=31536000');\n"; // 1 year cache
148 | r += "?>"+grunt.file.read("build/editor.min.css")+"<\/script>/,
157 | //replacement: function(){return '';}
158 | replacement: function(){return '';}
159 | },
160 | { // with php handler
161 | match: /\/\/ @@js/,
162 | replacement: function(){
163 | var r = "if(isset($_GET['js'])){";
164 | r += "header('Content-Type: application/javascript');\n";
165 | r += "header('Cache-Control: public, maxage=31536000');\n"; // 1 year cache
166 | r += "?>"+grunt.file.read("build/editor.min.js")+"
14 | &1');
32 | exit;
33 | }
34 | // @@css
35 | // @@js
36 | require('util.php');
37 | $PATH=isset($_REQUEST["p"])?$_REQUEST["p"]:'';
38 | if($PATH==='' && $_SERVER['REQUEST_METHOD']==='GET'){if(!$DEFAULT_DIR)$DEFAULT_DIR=realpath('.');header('Location: ?p='.urlencodelite($DEFAULT_DIR));exit;}
39 |
40 | header('Cache-Control: no-store'); // To disable caching on browser back button.
41 |
42 | if($ALLOW_SHELL && isset($_POST["ajaxShell"])){
43 | $COMMAND=$_POST["ajaxShell"];
44 | $TEMP_DIR_WITH_TRAILING_SLASH='/tmp/';
45 | if(isset($_SERVER['TEMP'])){
46 | $TEMP_DIR_WITH_TRAILING_SLASH=$_SERVER['TEMP'];
47 | if($WINDOWS)$TEMP_DIR_WITH_TRAILING_SLASH.='\\';
48 | else $TEMP_DIR_WITH_TRAILING_SLASH.='/';
49 | }
50 | $STDOUT_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.STDOUT';
51 | $RESULT_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.RESULT';
52 | $LASTCMD_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.LASTCMD';
53 | $LOCK_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.LOCK';
54 | $tempExecuteFile=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.'.($WINDOWS?'cmd':'sh');
55 | $WD=$PATH;
56 | if($WINDOWS)$WD=str_replace('/','\\',$WD);
57 | $lastCmd=null;
58 | if(file_exists($LASTCMD_FILE))$lastCmd=file_get_contents($LASTCMD_FILE);
59 | header('Content-Type: application/json');
60 |
61 | if(file_exists($LOCK_FILE)){
62 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
63 | echo json_encode(array("continue"=>TRUE,"output"=>$output,"lastCmd"=>$lastCmd,"debugLockFile"=>$LOCK_FILE));
64 | }
65 | elseif(file_exists($STDOUT_FILE)){
66 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
67 | $result=file_get_contents($RESULT_FILE);
68 | unlink($STDOUT_FILE);
69 | unlink($RESULT_FILE);
70 | echo json_encode(array("continue"=>FALSE,"output"=>$output,"lastCmd"=>$lastCmd,"result"=>$result));
71 | }
72 | elseif($COMMAND){
73 | $output=null;
74 | file_put_contents($LOCK_FILE,'');
75 | file_put_contents($LASTCMD_FILE,$COMMAND);
76 | if($WINDOWS){ // No async for windows yet
77 | file_put_contents($tempExecuteFile,"cd \"".$WD."\" || exit 1\n".$SHELL_PRE."\n".$COMMAND." >>".$STDOUT_FILE."\ndel ".$LASTCMD_FILE."\ndel ".$LOCK_FILE."\ndel ".$tempExecuteFile);
78 | //shell_exec('START /B CMD /C CALL \"'+$tempExecuteFile.'\"');
79 | shell_exec($tempExecuteFile);
80 | //usleep(100000); // So we can see some output on the first round.
81 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
82 | }
83 | else{
84 | file_put_contents($tempExecuteFile,"#!/bin/bash\ncd ".escapeshellarg($WD)." || exit 1\n".$SHELL_PRE."\n{ ".$COMMAND."; } 2>&1\nRESULT=$?\nrm ".$LASTCMD_FILE."\nrm ".$LOCK_FILE."\nrm ".$tempExecuteFile."\n[[ \$RESULT == 0 ]] && printf success >$RESULT_FILE || printf failure >$RESULT_FILE");
85 | shell_exec('/bin/bash '.$tempExecuteFile.' >>'.$STDOUT_FILE.' &');
86 | //usleep(100000); // So we can see some output on the first round.
87 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
88 | }
89 | echo json_encode(array("first"=>TRUE,"continue"=>TRUE,"output"=>$output
90 | ,"debug_tempExecuteFile"=>$tempExecuteFile,"debug_command"=>$COMMAND,"debug_stdout"=>$STDOUT_FILE
91 | ));
92 | }
93 | else{
94 | // Nothing to do.
95 | echo json_encode(array("idle"=>TRUE));
96 | }
97 | exit();
98 | }
99 |
100 | require('commands.php');
101 |
102 | $Recursive=FALSE;
103 | if(isset($_REQUEST["r"]))$Recursive=TRUE;
104 | $Grep="";
105 | if(isset($_REQUEST["grep"]))$Grep=$_REQUEST["grep"];
106 | $Find="";
107 | if(isset($_REQUEST["find"]))$Find=$_REQUEST["find"];
108 | $Locate="";
109 | if(isset($_REQUEST["locate"]))$Locate=$_REQUEST["locate"];
110 | $Title=substr($PATH,strrpos(str_replace('\\','/',$PATH),'/')+1);
111 | if($Title=='')$Title='Code Editor';
112 | ?>
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
162 | ";
174 | $html.="Save ";
175 | $html.="Save & Close ";
176 | $html.=" ";
177 | $c=file_get_contents($PATH);
178 | if(substr($c,0,3)==pack("CCC",0xef,0xbb,0xbf)) // Remove BOM
179 | $c=substr($c,3);
180 | $html.="" . htmlentities($c,ENT_SUBSTITUTE) . "
";
181 | return $html;
182 | }
183 | function getFiles(){
184 | global $PATH,$Recursive,$Locate,$DIRS_AT_TOP;
185 | if($Locate)
186 | $r=getFilesUsingLocate($PATH,$Locate);
187 | elseif($Recursive)
188 | $r=scandir_recursive($PATH);
189 | else
190 | $r=scandir($PATH);
191 | sort($r,SORT_STRING|SORT_FLAG_CASE);
192 |
193 | if($DIRS_AT_TOP && !$Locate && !$Recursive){
194 | $files=array();
195 | $dirs=array();
196 | foreach($r as $f)
197 | {
198 | if(is_dir($PATH.'/'.$f))
199 | array_push($dirs,$f);
200 | else
201 | array_push($files,$f);
202 | }
203 | $r=array_merge($dirs,$files);
204 | }
205 | return $r;
206 | }
207 | function renderFileList()
208 | {
209 | global $PATH,$Grep,$Find,$Recursive;
210 | $editorPPath=realpath($_SERVER["DOCUMENT_ROOT"]);
211 | $files=getFiles();
212 | $html="";
213 | $html.="";
214 | if ($PATH != ""){
215 | $PathEscaped=str_replace("'","\\'",$PATH);
216 | $delOnclick="onclick=\""."if(editor.del('$PathEscaped',null)===true){window.location=$('#list>.dir:first a:first').attr('href');}return(false)\"";
217 | if(get_dir_count($PATH)>0&&!is_link($PATH)) # Hide del button if dir not empty and not a symlink.
218 | $delOnclick='';
219 | $up=substr($PATH,0, strrpos($PATH,'/'));
220 | if($up==='')
221 | $up='/';
222 | elseif($Recursive)
223 | $up=$PATH; # Back out of search/recursive mode instead of up a level.
224 | $html.="
";
225 | }
226 | foreach($files as $filePath)
227 | {
228 | $rFile=$filePath;
229 | $aFile=($PATH=='/'?'':$PATH).'/'.$rFile;
230 | $pFile=realpath($aFile);
231 | if($rFile=='.'||$rFile=='..')
232 | continue;
233 | $isDir=is_dir($aFile);
234 |
235 |
236 | if($Find){
237 | if(preg_match('/'.$Find.'/i',$rFile)!==1)
238 | continue;
239 | }
240 | if($Grep!=''){
241 | if($isDir)continue;
242 | $fileContents=file_get_contents($aFile,false,null,0,5242880); // Limit to 5MB
243 | if(strpos(strtolower($fileContents),strtolower($Grep))===FALSE)
244 | continue;
245 | }
246 |
247 | if($isDir){
248 | $size=get_dir_count($aFile);
249 | $friendlySize=$size;
250 | }
251 | else{
252 | $size=filesize($aFile);
253 | $friendlySize=human_readable_filesize(filesize($aFile));
254 | }
255 | $age=human_readable_timespan(time()-filemtime($aFile));
256 | $direct='';
257 | if(strpos($pFile,$editorPPath)===0){
258 | $direct=urlencodelite(str_replace('\\','/',substr($pFile,strlen($editorPPath))));
259 | if($direct=='')$direct='/';
260 | }
261 | $aFileEscaped=str_replace("'","\\'",$aFile);
262 | if($isDir)
263 | $dlAnchor='';
264 | else
265 | $dlAnchor='
';
266 | $delOnclick="onclick=\"editor.del('$aFileEscaped',this);return(false)\"";
267 | if($isDir&&$size!==0&&!is_link($aFile))$delOnclick=''; # Hide del button if dir not empty and not a symlink.
268 |
269 | $segAs='';
270 |
271 | // Anchor each path segment.
272 | $segs=explode('/',$rFile);
273 | $segsCount=count($segs);
274 | $segAppend='';
275 | for($i=0;$i<$segsCount;$i++){
276 | $segAppend.='/'.$segs[$i];
277 | if($i===$segsCount-1 && ($size>=1048576 || preg_match('/\.(mp3|aac|ogg|wav|mid|jpg|bmp|gif|png|webp|webm|mp4|mkv|m4v|avi|pdf|zip|rar|tar|gz|7z)$/i',$rFile)===1))
278 | // Don't allow editing if file is over 1MB or is a media type.
279 | $href=$direct;
280 | else
281 | $href='?p='.urlencodelite(($PATH==='/'?'':$PATH).$segAppend);
282 | $segIsDir=$i!==$segsCount-1;
283 | $segAs.='
/ '.$segs[$i].' ';
284 | }
285 | $segAs=substr($segAs,28); // Trim leading "
/ "
286 |
287 | $pasteAnchor='';
288 | if($isDir)
289 | $pasteAnchor="
";
290 |
291 | $html.='
'.$segAs."
$friendlySize $age $dlAnchor$pasteAnchor
";
292 | }
293 | $html.="
";
294 | return $html;
295 | }
296 | if(isset($BRANDING_FOOTER))echo $BRANDING_FOOTER;
297 | ?>
298 |
299 | $(function(){$(".shellButton").triggerHandler("click");});'; // If was postback then shell form is already open so need to trigger click to init form events ?>
300 |
301 |
302 |
--------------------------------------------------------------------------------
/src/editor.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | 'use strict';
3 | var Path = '',
4 | editor = {};
5 | try {
6 | if (window.location.search.search(new RegExp('[?&]p=([^&$]*)', 'i')) !== -1) {
7 | Path = RegExp.$1;
8 | }
9 | Path = decodeURIComponent(Path);
10 | } catch (e) {
11 | alert(e);
12 | alert('Path=' + Path);
13 | }
14 | window.editor = editor;
15 | editor.detectFileMode = function(filePath) {
16 | if (/^\/etc\/apache2\//.test(filePath)) {
17 | return 'apache_conf';
18 | } else if (/Dockerfile$/.test(filePath)) {
19 | return 'dockerfile';
20 | }
21 | var ext = filePath.substring(filePath.lastIndexOf('.') + 1);
22 | // https://github.com/ajaxorg/ace/tree/master/lib/ace/mode
23 | switch (ext) {
24 | case 'css':
25 | return 'css';
26 | case 'js':
27 | return 'javascript';
28 | case 'json':
29 | return 'json';
30 | case 'asax':
31 | case 'ashx':
32 | case 'cs':
33 | return 'csharp';
34 | case 'xml':
35 | return 'xml';
36 | case 'phtml':
37 | case 'php':
38 | return 'php';
39 | case 'config':
40 | return 'xml';
41 | case 'as':
42 | return 'actionscript';
43 | case 'bat':
44 | case 'cmd':
45 | return 'batchfile';
46 | case 'c':
47 | case 'h':
48 | case 'hpp':
49 | case 'cpp':
50 | return 'c_cpp';
51 | case 'coffee':
52 | return 'coffee';
53 | case 'dart':
54 | return 'dart';
55 | case 'diff':
56 | return 'diff';
57 | case 'asp':
58 | case 'asa':
59 | case 'aspx':
60 | case 'ascx':
61 | case 'htm':
62 | case 'html':
63 | return 'html';
64 | case 'ini':
65 | return 'ini';
66 | case 'java':
67 | return 'java';
68 | case 'jsp':
69 | return 'jsp';
70 | case 'less':
71 | return 'less';
72 | case 'lua':
73 | return 'lua';
74 | case 'pl':
75 | return 'perl';
76 | case 'ps':
77 | return 'powershell';
78 | case 'py':
79 | return 'python';
80 | case 'cgi':
81 | case 'sh':
82 | return 'sh';
83 | case 'sql':
84 | return 'sql';
85 | case 'svg':
86 | return 'svg';
87 | case 'md':
88 | return 'markdown';
89 | default:
90 | return '';
91 | }
92 | };
93 | editor.detectFileModeByContent = function(content) {
94 | if (content.indexOf('#!/bin/sh') === 0) {
95 | return 'sh';
96 | }
97 | if (content.indexOf('#!/bin/bash') === 0) {
98 | return 'sh';
99 | }
100 | return '';
101 | };
102 | editor.save = function(close) {
103 | jQuery.ajax({
104 | url: '?',
105 | type: 'post',
106 | dataType: 'json',
107 | data: {
108 | content: editor.instance.getValue(),
109 | p: Path
110 | },
111 | success: function() {
112 | if (close) {
113 | history.back();
114 | } else {
115 | if (editor.isModified) {
116 | editor.isModified = false;
117 | document.title = document.title.substring(1); // remove leading '*'
118 | }
119 | }
120 | },
121 | error: function(r) {
122 | alert(r.responseText);
123 | }
124 | });
125 | };
126 | editor.clip = {
127 | getItems: function() {
128 | var j = localStorage.clipValue;
129 | return j ? JSON.parse(j) : [];
130 | },
131 | setItems: function(items) {
132 | localStorage.clipValue = JSON.stringify(items);
133 | },
134 | add: function(path, type) {
135 | var i = this.getItems();
136 | i.push({
137 | path: path,
138 | type: type
139 | });
140 | this.setItems(i);
141 | },
142 | remove: function(o) {
143 | var path = o,
144 | item,
145 | items = editor.clip.getItems();
146 | if (typeof (path) === 'object') {
147 | path = o.path;
148 | }
149 | item = items.filter(function(x) {
150 | return x.path === path;
151 | })[0];
152 | if (!item) {
153 | return false;
154 | }
155 | items.splice(items.indexOf(item), 1);
156 | this.setItems(items);
157 | return true;
158 | }
159 | };
160 | editor.del = function(aPath, trigger) {
161 | var success = false,
162 | div = $(trigger).parent('div');
163 | if (div.length === 0) {
164 | console.error('DEBUG: Cannot find row in DOM.');
165 | }
166 | $.ajax({
167 | url: '?',
168 | method: 'post',
169 | data: {
170 | p: aPath,
171 | rm: 1
172 | },
173 | async: false,
174 | success: function(r) {
175 | if (r.success === true) {
176 | div.remove();
177 | success = true;
178 | } else {
179 | alert(r.message);
180 | }
181 | },
182 | error: function(r) {
183 | alert(r.responseText);
184 | }
185 | });
186 | return success;
187 | };
188 | editor.cut = function(aPath) {
189 | editor.clip.add(aPath, 'cut');
190 | console.log('Clipboard: ' + aPath);
191 | };
192 | editor.copy = function(aPath) {
193 | editor.clip.add(aPath, 'copy');
194 | console.log('Clipboard: ' + aPath);
195 | };
196 | editor.paste = function(clipItemPath, dest) {
197 | var item,
198 | pasteAs,
199 | data;
200 | if (!clipItemPath) {
201 | clipItemPath = editor.clip.getItems()[0].path;
202 | }
203 | item = editor.clip.getItems().filter(function(x) {
204 | return x.path === clipItemPath;
205 | })[0];
206 | pasteAs = prompt(
207 | item.type === 'copy' ? 'Copy to:' : 'Move to:', item.path.substring(1 + item.path.lastIndexOf('/')));
208 | if (!pasteAs) {
209 | return;
210 | }
211 | dest += '/' + pasteAs;
212 | data = {
213 | source: item.path,
214 | dest: dest
215 | };
216 | if (item.type === 'copy') {
217 | data.cp = 1;
218 | } else if (item.type === 'cut') {
219 | data.mv = 1;
220 | }
221 | if (dest) {
222 | $.ajax({
223 | url: '?',
224 | method: 'post',
225 | data: data,
226 | success: function(r) {
227 | if (r.success === true) {
228 | editor.clip.remove(clipItemPath);
229 | window.location = window.location;
230 | } else {
231 | alert(r.message);
232 | }
233 | },
234 | error: function(r) {
235 | alert(r.responseText);
236 | }
237 | });
238 | }
239 | };
240 | editor.instance = null;
241 | (function() {
242 | var clipItems,
243 | list,
244 | mode;
245 | $('#editor').each(function() {
246 | $('header.list').hide();
247 | mode = editor.detectFileMode(Path);
248 | if (!mode) {
249 | mode = editor.detectFileModeByContent(document.getElementById('editor').textContent.trim());
250 | }
251 | editor.instance = ace.edit('editor');
252 | //editor.instance.setTheme('ace/theme/monokai');
253 | if ($(this).get(0).hasAttribute('data-readonly')) {
254 | document.title += ' [readonly]';
255 | editor.instance.setOptions({
256 | readOnly: true,
257 | highlightActiveLine: false,
258 | highlightGutterLine: false
259 | });
260 | }
261 | editor.instance.on('change', function() {
262 | if (!editor.isModified) {
263 | editor.isModified = true;
264 | document.title = '*' + document.title;
265 | }
266 | });
267 | if (mode) {
268 | if (typeof (console) !== 'undefined') {
269 | console.log('Setting editor mode: ' + 'ace/mode/' + mode);
270 | }
271 | editor.instance.getSession().setMode('ace/mode/' + mode);
272 | } else {
273 | if (typeof (console) !== 'undefined') {
274 | console.log('Unsupported editor mode. All available modes here: https://github.com/ajaxorg/ace-builds/tree/master/src-noconflict');
275 | }
276 | }
277 | editor.instance.commands.addCommand({
278 | name: 'Save',
279 | bindKey: {
280 | win: 'Ctrl-s',
281 | mac: 'Command-s'
282 | },
283 | exec: function() {
284 | editor.save(false);
285 | }
286 | });
287 | });
288 |
289 | // Remove any hashes from URL.
290 | if (window.location.hash) {
291 | window.location = window.location.search;
292 | }
293 |
294 | // Append clipboard files to the list.
295 | clipItems = editor.clip.getItems();
296 | list = $('#list');
297 | clipItems.forEach(function(clipItem) {
298 | var a = $(' ').text(clipItem.path),
299 | pasteButton = $(' ').addClass('paste').attr('href', '#'),
300 | removeButton = $(' ').addClass('remove-clip').attr('href', '#');
301 | list.append($('
').addClass('clip').append(a).append(pasteButton).append(removeButton));
302 | pasteButton.click(function() {
303 | editor.paste(clipItem.path, Path);
304 | return false;
305 | });
306 | removeButton.click(function() {
307 | if (editor.clip.remove(clipItem.path)) {
308 | $(this).parent().remove();
309 | }
310 | return false;
311 | });
312 | });
313 |
314 | // Show the paste button for all directories if there is an item in the clipboard.
315 | if (clipItems.length > 0) {
316 | $('#list>div.dir>a.paste').css({
317 | display: 'table-cell'
318 | });
319 | }
320 | }());
321 |
322 | // Keyboard shortcuts.
323 | $(document).keydown(function(e) {
324 | if (e.keyCode === 115) { // F4
325 | $('.shellButton').click();
326 | }
327 | });
328 | $(function() {
329 | $('.searchForm select').change(function() {
330 | if ($(this).val() === 'Locate Database') {
331 | $('.searchForm input[type=checkbox]').prop('checked', false).prop('disabled', true);
332 | } else {
333 | $('.searchForm input[type=checkbox]').prop('disabled', false);
334 | }
335 | });
336 | $('.searchForm button').click(function() {
337 | var select = $('.searchForm select').val(),
338 | url = '?p=' + encodeURIComponent(Path).replace(/%2F/g, '/');
339 | if ($('.searchForm input[type=checkbox]').prop('checked')) {
340 | url += '&r=';
341 | }
342 | if (select === 'Filenames' || select === 'All') {
343 | url += '&find=' + encodeURIComponent($('.searchForm input[type=text]').val()).replace(/%2F/g, '/');
344 | }
345 | if (select === 'Content (All Files)' || select === 'All') {
346 | url += '&grep=' + encodeURIComponent($('.searchForm input[type=text]').val()).replace(/%2F/g, '/');
347 | }
348 | if (select === 'Content (Code Only)') {
349 | url += '&find=^[^.]*$|\\.(php|js|json|..?ss|p?html?|as..?|cs|vb|rb|py|txt|md|xml|xslt?|config)$&grep=' + encodeURIComponent($('.searchForm input[type=text]').val()).replace(/%2F/g, '/');
350 | }
351 | if (select === 'Locate Database') {
352 | url += '&locate=' + encodeURIComponent($('.searchForm input[type=text]').val().replace(/[ ]+/g, '.*')).replace(/%2F/g, '/');
353 | }
354 | window.location = url;
355 | });
356 | $('.newButton').click(function() {
357 | var v = prompt('New file (end with / for dir):', 'new.txt'),
358 | type = 'file';
359 | //if(v.endsWith('/')){
360 | if (v.substring(v.length - 1) === '/') {
361 | type = 'dir';
362 | v = v.substring(0, v.length - 1);
363 | }
364 | if (v) {
365 | $.ajax({
366 | url: '?',
367 | method: 'post',
368 | data: {
369 | new: 1,
370 | p: Path + '/' + v,
371 | type: type
372 | },
373 | dataType: 'json',
374 | success: function(r) {
375 | if (r.success === true) {
376 | window.location = '?p=' + encodeURIComponent(Path + '/' + v).replace(/%2F/g, '/');
377 | } else {
378 | alert(r.message);
379 | }
380 | },
381 | error: function(r) {
382 | alert(r.responseText);
383 | }
384 | });
385 | }
386 | });
387 | $('.searchButton').click(function() {
388 | $(this).addClass('active');
389 | $('.searchForm').show().find('input[type=text]:first').select();
390 | });
391 | $('.shellButton').click(function() {
392 | $(this).addClass('active');
393 | var input = $('.shellForm').show().find('input[type=text]:first').select(),
394 | first = true,
395 | continuingLastSession = false,
396 | blinkOn = true,
397 | callNext;
398 | callNext = function(cmd) {
399 | if (typeof (cmd) === 'undefined') {
400 | cmd = null;
401 | }
402 | $.ajax({
403 | url: '?',
404 | type: 'post',
405 | dataType: 'json',
406 | data: {
407 | 'ajaxShell': cmd,
408 | p: Path
409 | },
410 | success: function(r) {
411 | if (r.lastCmd) {
412 | input.val(r.lastCmd).prop('readonly', true);
413 | }
414 | if (r.idle === true) {
415 | input.prop('readonly', false);
416 | return;
417 | }
418 | if (first === true && r.first !== true) {
419 | continuingLastSession = true;
420 | $('.shellForm input#shellFormBackground').prop('checked', true);
421 | }
422 | first = false;
423 | blinkOn = !blinkOn;
424 | $('#shellOutput').html((continuingLastSession ? 'Continuing last session...\n\n' : '') + r.output + (r.continue === true ? (blinkOn ? '_' : ' ') : ''));
425 | if (r.continue === true) {
426 | setTimeout(callNext, 500);
427 | } else { // Command finished
428 | if (r.result === 'failure') {
429 | $('.shellForm').css({
430 | backgroundColor: '#f44'
431 | });
432 | alert('Last command failed.');
433 | } else if (r.result === 'success') {
434 | $('.shellForm').css({
435 | backgroundColor: '#6f6'
436 | });
437 | } else {
438 | $('.shellForm').css({
439 | backgroundColor: '#bbb'
440 | });
441 | alert('Last command had no result.');
442 | }
443 | window.onbeforeunload = null;
444 | input.prop('readonly', false).select();
445 | }
446 | }
447 | });
448 | };
449 | callNext();
450 | $('.shellForm').submit(function() {
451 | var shellHistory = JSON.parse(localStorage.shellHistory || '[]');
452 | shellHistory.push(input.val());
453 | localStorage.shellHistory = JSON.stringify(shellHistory);
454 |
455 | setTimeout(function() {
456 | window.onbeforeunload = function() {
457 | return 'You have a shell command running.';
458 | };
459 | }, 500);
460 |
461 | if (!$('.shellForm input#shellFormBackground').prop('checked')) {
462 | return true; // Normal form submit.
463 | }
464 | $('.shellForm').css({
465 | backgroundColor: '#fff'
466 | }); // prepare background for ajax command where result will set red or green background
467 | callNext(input.val());
468 | return false;
469 | });
470 | });
471 | $('#list .dir a[class!="seg"], #list .file a[class!="seg"]').click(function(e) {
472 | e.stopPropagation();
473 | });
474 |
475 | // Full row click.
476 | $('#list .dir, #list .file').click(function() {
477 | $(this).find('a.seg:last').get(0).click();
478 | });
479 |
480 | var buildShellHistoryDataList = function() {
481 | var datalist = $('#shellHistory'),
482 | shellHistory = JSON.parse(localStorage.shellHistory || '[]'),
483 | i,
484 | item;
485 | for (i = 0; i < shellHistory.length, item = shellHistory[i]; i += 1) {
486 | datalist.append($(' ').attr('value', item));
487 | }
488 | };
489 | buildShellHistoryDataList();
490 |
491 | var uploadDialog = null;
492 | var dropzoneProgress = function(percent) {
493 | if (uploadDialog === null) {
494 | uploadDialog = $(' ').text('Uploading ').append($(''));
495 | $(document.body).append(uploadDialog);
496 | uploadDialog.get(0).showModal();
497 | }
498 | var progress = uploadDialog.find('progress').get(0);
499 | progress.value = percent;
500 | if (percent === 100) {
501 | alert('Upload Complete!');
502 | window.location = window.location;
503 | }
504 | };
505 | $(document.body).dropzone({
506 | url: '?p=' + Path,
507 | clickable: false,
508 | previewsContainer: 'body',
509 | totaluploadprogress: dropzoneProgress
510 | });
511 | $('.uploadButton').dropzone({
512 | url: '?p=' + Path,
513 | clickable: true,
514 | previewsContainer: 'body',
515 | totaluploadprogress: dropzoneProgress
516 | });
517 | });
518 | })(jQuery);
519 |
--------------------------------------------------------------------------------
/dist/editor.php:
--------------------------------------------------------------------------------
1 |
14 |
23 |
24 |
25 |
26 | Code Editor - Set Password
27 |
28 | ",FILE_APPEND);
31 | header('Location: '.$_SERVER['SCRIPT_NAME']);
32 | }
33 | ?>
34 |
35 |
36 |
46 |
47 |
56 |
57 |
58 |
76 |
77 |
82 |
83 |
84 |
85 | Code Editor - Login
86 |
87 |
97 |
98 |
115 |
116 |
117 |
125 |
126 | &1');
132 | exit;
133 | }
134 | if(isset($_GET['css'])){header('Content-Type: text/css');
135 | header('Cache-Control: public, maxage=31536000');
136 | ?>#list,header nav .active{font-weight:700}#list>div,form{border-top:1px solid #444}body{font-family:sans-serif;font-size:11px;margin:0}a{text-decoration:none}input[type=text]{border:1px solid #444}header nav a{float:left;padding:6px 7px;color:#444}header nav a:hover{background-color:#aaa}header nav:after{content:"";clear:both;display:table}form{display:none;padding:10px}form .fieldRow{margin-bottom:5px}pre{margin:0 0 10px}input[readonly]{background-color:#ddd;opacity:.3}#list{border-bottom:1px solid #444}#list>div{width:100%;display:table}#list .copy,#list .cut,#list .del,#list .direct,#list .dl,#list .paste,#list .remove-clip{width:16px;min-width:16px;background-size:100%;background-position:50% 50%;background-repeat:no-repeat;background-color:#fff}#list>div>*{display:table-cell;padding:2px 0 2px 3px}#list .del:not([onclick]),#list .dir .paste,#list .direct[href=""]{display:none}#list>div>a .indent{margin-left:10px}#list>div.bad{text-decoration:line-through}#list .cut{background-image:url(//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/cut.png)}#list .copy{background-image:url(//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/page_copy.png)}#list .paste{background-image:url(//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/page_paste.png)}#list .del,#list .remove-clip{background-image:url(//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/delete.png)}#list .direct{background-image:url(//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/world_go.png)}#list .dl{background-image:url(//cdnjs.cloudflare.com/ajax/libs/fatcow-icons/20130425/FatCow_Icons32x32/inbox_download.png)}#list>div:nth-of-type(odd){background-color:#dfd}#list>div:hover,#list>div:nth-of-type(odd):hover{background-color:#ddd}#list .seg:hover{text-decoration:underline;color:#444!important}#list .seg[href=""]:hover{text-decoration:none;color:#ccc!important}#list .dir,#list .file{cursor:pointer}#list .filepath .slash{color:#bbb}#list .dir .filepath,#list .dir .seg{color:#bb0}#list .file .filepath,#list .file .seg{color:#1b0}#list .file .seg.d{color:#bb0}#list .seg[href=""]{color:#ccc;cursor:default}#list .age,#list .size{color:#bbb;font-size:10px;width:100px}#list .size{margin-left:5px}#list .dir .size:after{content:" files"}#list .age:after{content:" old"}@media (max-width:979px){body{font-size:12px}#list>div>*{display:block;padding-bottom:0}#list .age,#list .size{float:left;height:16px;height:auto;padding:0 0 4px}#list .copy,#list .cut,#list .del,#list .direct,#list .dl,#list .paste,#list .remove-clip{float:right;height:17px;width:17px;padding:0}}button{border:none;margin:0;font-size:10px}#editor{margin-top:15px;position:absolute;top:0;bottom:0;left:0;right:0}!function(a){"use strict";var b="",c={};try{-1!==window.location.search.search(new RegExp("[?&]p=([^&$]*)","i"))&&(b=RegExp.$1),b=decodeURIComponent(b)}catch(d){alert(d),alert("Path="+b)}window.editor=c,c.detectFileMode=function(a){if(/^\/etc\/apache2\//.test(a))return"apache_conf";if(/Dockerfile$/.test(a))return"dockerfile";var b=a.substring(a.lastIndexOf(".")+1);switch(b){case"css":return"css";case"js":return"javascript";case"json":return"json";case"asax":case"ashx":case"cs":return"csharp";case"xml":return"xml";case"phtml":case"php":return"php";case"config":return"xml";case"as":return"actionscript";case"bat":case"cmd":return"batchfile";case"c":case"h":case"hpp":case"cpp":return"c_cpp";case"coffee":return"coffee";case"dart":return"dart";case"diff":return"diff";case"asp":case"asa":case"aspx":case"ascx":case"htm":case"html":return"html";case"ini":return"ini";case"java":return"java";case"jsp":return"jsp";case"less":return"less";case"lua":return"lua";case"pl":return"perl";case"ps":return"powershell";case"py":return"python";case"cgi":case"sh":return"sh";case"sql":return"sql";case"svg":return"svg";case"md":return"markdown";default:return""}},c.detectFileModeByContent=function(a){return 0===a.indexOf("#!/bin/sh")?"sh":0===a.indexOf("#!/bin/bash")?"sh":""},c.save=function(a){jQuery.ajax({url:"?",type:"post",dataType:"json",data:{content:c.instance.getValue(),p:b},success:function(){a?history.back():c.isModified&&(c.isModified=!1,document.title=document.title.substring(1))},error:function(a){alert(a.responseText)}})},c.clip={getItems:function(){var a=localStorage.clipValue;return a?JSON.parse(a):[]},setItems:function(a){localStorage.clipValue=JSON.stringify(a)},add:function(a,b){var c=this.getItems();c.push({path:a,type:b}),this.setItems(c)},remove:function(a){var b,d=a,e=c.clip.getItems();return"object"==typeof d&&(d=a.path),(b=e.filter(function(a){return a.path===d})[0])?(e.splice(e.indexOf(b),1),this.setItems(e),!0):!1}},c.del=function(b,c){var d=!1,e=a(c).parent("div");return 0===e.length&&console.error("DEBUG: Cannot find row in DOM."),a.ajax({url:"?",method:"post",data:{p:b,rm:1},async:!1,success:function(a){a.success===!0?(e.remove(),d=!0):alert(a.message)},error:function(a){alert(a.responseText)}}),d},c.cut=function(a){c.clip.add(a,"cut"),console.log("Clipboard: "+a)},c.copy=function(a){c.clip.add(a,"copy"),console.log("Clipboard: "+a)},c.paste=function(b,d){var e,f,g;b||(b=c.clip.getItems()[0].path),e=c.clip.getItems().filter(function(a){return a.path===b})[0],f=prompt("copy"===e.type?"Copy to:":"Move to:",e.path.substring(1+e.path.lastIndexOf("/"))),f&&(d+="/"+f,g={source:e.path,dest:d},"copy"===e.type?g.cp=1:"cut"===e.type&&(g.mv=1),d&&a.ajax({url:"?",method:"post",data:g,success:function(a){a.success===!0?(c.clip.remove(b),window.location=window.location):alert(a.message)},error:function(a){alert(a.responseText)}}))},c.instance=null,function(){var d,e,f;a("#editor").each(function(){a("header.list").hide(),f=c.detectFileMode(b),f||(f=c.detectFileModeByContent(document.getElementById("editor").textContent.trim())),c.instance=ace.edit("editor"),a(this).get(0).hasAttribute("data-readonly")&&(document.title+=" [readonly]",c.instance.setOptions({readOnly:!0,highlightActiveLine:!1,highlightGutterLine:!1})),c.instance.on("change",function(){c.isModified||(c.isModified=!0,document.title="*"+document.title)}),f?("undefined"!=typeof console&&console.log("Setting editor mode: ace/mode/"+f),c.instance.getSession().setMode("ace/mode/"+f)):"undefined"!=typeof console&&console.log("Unsupported editor mode. All available modes here: https://github.com/ajaxorg/ace-builds/tree/master/src-noconflict"),c.instance.commands.addCommand({name:"Save",bindKey:{win:"Ctrl-s",mac:"Command-s"},exec:function(){c.save(!1)}})}),window.location.hash&&(window.location=window.location.search),d=c.clip.getItems(),e=a("#list"),d.forEach(function(d){var f=a(" ").text(d.path),g=a(" ").addClass("paste").attr("href","#"),h=a(" ").addClass("remove-clip").attr("href","#");e.append(a("
").addClass("clip").append(f).append(g).append(h)),g.click(function(){return c.paste(d.path,b),!1}),h.click(function(){return c.clip.remove(d.path)&&a(this).parent().remove(),!1})}),d.length>0&&a("#list>div.dir>a.paste").css({display:"table-cell"})}(),a(document).keydown(function(b){115===b.keyCode&&a(".shellButton").click()}),a(function(){a(".searchForm select").change(function(){"Locate Database"===a(this).val()?a(".searchForm input[type=checkbox]").prop("checked",!1).prop("disabled",!0):a(".searchForm input[type=checkbox]").prop("disabled",!1)}),a(".searchForm button").click(function(){var c=a(".searchForm select").val(),d="?p="+encodeURIComponent(b).replace(/%2F/g,"/");a(".searchForm input[type=checkbox]").prop("checked")&&(d+="&r="),"Filenames"!==c&&"All"!==c||(d+="&find="+encodeURIComponent(a(".searchForm input[type=text]").val()).replace(/%2F/g,"/")),"Content (All Files)"!==c&&"All"!==c||(d+="&grep="+encodeURIComponent(a(".searchForm input[type=text]").val()).replace(/%2F/g,"/")),"Content (Code Only)"===c&&(d+="&find=^[^.]*$|\\.(php|js|json|..?ss|p?html?|as..?|cs|vb|rb|py|txt|md|xml|xslt?|config)$&grep="+encodeURIComponent(a(".searchForm input[type=text]").val()).replace(/%2F/g,"/")),"Locate Database"===c&&(d+="&locate="+encodeURIComponent(a(".searchForm input[type=text]").val().replace(/[ ]+/g,".*")).replace(/%2F/g,"/")),window.location=d}),a(".newButton").click(function(){var c=prompt("New file (end with / for dir):","new.txt"),d="file";"/"===c.substring(c.length-1)&&(d="dir",c=c.substring(0,c.length-1)),c&&a.ajax({url:"?",method:"post",data:{"new":1,p:b+"/"+c,type:d},dataType:"json",success:function(a){a.success===!0?window.location="?p="+encodeURIComponent(b+"/"+c).replace(/%2F/g,"/"):alert(a.message)},error:function(a){alert(a.responseText)}})}),a(".searchButton").click(function(){a(this).addClass("active"),a(".searchForm").show().find("input[type=text]:first").select()}),a(".shellButton").click(function(){a(this).addClass("active");var c,d=a(".shellForm").show().find("input[type=text]:first").select(),e=!0,f=!1,g=!0;c=function(h){"undefined"==typeof h&&(h=null),a.ajax({url:"?",type:"post",dataType:"json",data:{ajaxShell:h,p:b},success:function(b){return b.lastCmd&&d.val(b.lastCmd).prop("readonly",!0),b.idle===!0?void d.prop("readonly",!1):(e===!0&&b.first!==!0&&(f=!0,a(".shellForm input#shellFormBackground").prop("checked",!0)),e=!1,g=!g,a("#shellOutput").html((f?"Continuing last session...\n\n":"")+b.output+(b["continue"]===!0?g?"_":" ":"")),void(b["continue"]===!0?setTimeout(c,500):("failure"===b.result?(a(".shellForm").css({backgroundColor:"#f44"}),alert("Last command failed.")):"success"===b.result?a(".shellForm").css({backgroundColor:"#6f6"}):(a(".shellForm").css({backgroundColor:"#bbb"}),alert("Last command had no result.")),window.onbeforeunload=null,d.prop("readonly",!1).select())))}})},c(),a(".shellForm").submit(function(){var b=JSON.parse(localStorage.shellHistory||"[]");return b.push(d.val()),localStorage.shellHistory=JSON.stringify(b),setTimeout(function(){window.onbeforeunload=function(){return"You have a shell command running."}},500),a(".shellForm input#shellFormBackground").prop("checked")?(a(".shellForm").css({backgroundColor:"#fff"}),c(d.val()),!1):!0})}),a('#list .dir a[class!="seg"], #list .file a[class!="seg"]').click(function(a){a.stopPropagation()}),a("#list .dir, #list .file").click(function(){a(this).find("a.seg:last").get(0).click()});var c=function(){var b,c,d=a("#shellHistory"),e=JSON.parse(localStorage.shellHistory||"[]");for(b=0;b ").attr("value",c))};c();var d=null,e=function(b){null===d&&(d=a(' ').text("Uploading ").append(a("")),a(document.body).append(d),d.get(0).showModal());var c=d.find("progress").get(0);c.value=b,100===b&&(alert("Upload Complete!"),window.location=window.location)};a(document.body).dropzone({url:"?p="+b,clickable:!1,previewsContainer:"body",totaluploadprogress:e}),a(".uploadButton").dropzone({url:"?p="+b,clickable:!0,previewsContainer:"body",totaluploadprogress:e})})}(jQuery);0 && $dir!='/')$relativePath=substr($fullPath,strlen($dir)+1);
151 | else $relativePath=$fullPath;
152 |
153 | // Remove trailing '/' as output should be relative to $dir.
154 | if($relativePath[0]=='/')
155 | $relativePath=substr($relativePath,1);
156 |
157 | array_push($r,$relativePath);
158 | }
159 | return $r;
160 | }
161 | function scandir_recursive($dir,$includeDirs=TRUE,$_prefix=''){
162 | $dir=rtrim($dir,'\\/');
163 | $r=array();
164 | foreach(scandir($dir)as$f){
165 | if($f!=='.'&&$f!=='..')
166 | if(is_dir("$dir/$f")){
167 | $r=array_merge($r,scandir_recursive("$dir/$f",$includeDirs,"$_prefix$f/"));
168 | if($includeDirs)$r[]=$_prefix.$f;
169 | }else $r[]=$_prefix.$f;
170 | }
171 | return $r;
172 | }
173 | function human_readable_filesize($v)
174 | {
175 | if($v>=1024*1024*1024)
176 | return (floor($v/(1024*1024*1024)*10)/10)." GB";
177 | elseif($v>=1024*1024)
178 | return (floor($v/(1024*1024)*10)/10)." MB";
179 | elseif($v>=1024)
180 | return (floor($v/1024*10)/10)." KB";
181 | else
182 | return $v." bytes";
183 | }
184 | function human_readable_timespan($v)
185 | {
186 | if($v>=60*60*24)
187 | return (floor($v/(60*60*24)*10)/10)." days";
188 | elseif($v>=60*60)
189 | return (floor($v/(60*60)*10)/10)." hours";
190 | elseif($v>=60)
191 | return floor($v/60)." min";
192 | else
193 | return $v." sec";
194 | }
195 | function get_dir_count($dir,$du=false){
196 | if(is_readable($dir)){
197 | if($du==TRUE){ // Recursively calculate the size of the directory's descendant items.
198 | $output=shell_exec('/usr/bin/find '.escapeshellarg($dir).' |wc -l');
199 | return intval($output)-1;
200 | }
201 | else{
202 | // Cound the items within the directory.
203 | return count(scandir($dir))-2; // -2 to exclude the "." and ".."
204 | }
205 | }
206 | else
207 | return null;
208 | }
209 | function urlencodelite($s){
210 | $s=rawurlencode($s);
211 | $s=str_replace('%2F','/',$s);
212 | $s=str_replace(' ','%20',$s);
213 | return $s;
214 | }
215 | $PATH=isset($_REQUEST["p"])?$_REQUEST["p"]:'';
216 | if($PATH==='' && $_SERVER['REQUEST_METHOD']==='GET'){if(!$DEFAULT_DIR)$DEFAULT_DIR=realpath('.');header('Location: ?p='.urlencodelite($DEFAULT_DIR));exit;}
217 |
218 | header('Cache-Control: no-store'); // To disable caching on browser back button.
219 |
220 | if($ALLOW_SHELL && isset($_POST["ajaxShell"])){
221 | $COMMAND=$_POST["ajaxShell"];
222 | $TEMP_DIR_WITH_TRAILING_SLASH='/tmp/';
223 | if(isset($_SERVER['TEMP'])){
224 | $TEMP_DIR_WITH_TRAILING_SLASH=$_SERVER['TEMP'];
225 | if($WINDOWS)$TEMP_DIR_WITH_TRAILING_SLASH.='\\';
226 | else $TEMP_DIR_WITH_TRAILING_SLASH.='/';
227 | }
228 | $STDOUT_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.STDOUT';
229 | $RESULT_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.RESULT';
230 | $LASTCMD_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.LASTCMD';
231 | $LOCK_FILE=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.LOCK';
232 | $tempExecuteFile=$TEMP_DIR_WITH_TRAILING_SLASH.'editor.php.'.($WINDOWS?'cmd':'sh');
233 | $WD=$PATH;
234 | if($WINDOWS)$WD=str_replace('/','\\',$WD);
235 | $lastCmd=null;
236 | if(file_exists($LASTCMD_FILE))$lastCmd=file_get_contents($LASTCMD_FILE);
237 | header('Content-Type: application/json');
238 |
239 | if(file_exists($LOCK_FILE)){
240 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
241 | echo json_encode(array("continue"=>TRUE,"output"=>$output,"lastCmd"=>$lastCmd,"debugLockFile"=>$LOCK_FILE));
242 | }
243 | elseif(file_exists($STDOUT_FILE)){
244 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
245 | $result=file_get_contents($RESULT_FILE);
246 | unlink($STDOUT_FILE);
247 | unlink($RESULT_FILE);
248 | echo json_encode(array("continue"=>FALSE,"output"=>$output,"lastCmd"=>$lastCmd,"result"=>$result));
249 | }
250 | elseif($COMMAND){
251 | $output=null;
252 | file_put_contents($LOCK_FILE,'');
253 | file_put_contents($LASTCMD_FILE,$COMMAND);
254 | if($WINDOWS){ // No async for windows yet
255 | file_put_contents($tempExecuteFile,"cd \"".$WD."\" || exit 1\n".$SHELL_PRE."\n".$COMMAND." >>".$STDOUT_FILE."\ndel ".$LASTCMD_FILE."\ndel ".$LOCK_FILE."\ndel ".$tempExecuteFile);
256 | //shell_exec('START /B CMD /C CALL \"'+$tempExecuteFile.'\"');
257 | shell_exec($tempExecuteFile);
258 | //usleep(100000); // So we can see some output on the first round.
259 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
260 | }
261 | else{
262 | file_put_contents($tempExecuteFile,"#!/bin/bash\ncd ".escapeshellarg($WD)." || exit 1\n".$SHELL_PRE."\n{ ".$COMMAND."; } 2>&1\nRESULT=$?\nrm ".$LASTCMD_FILE."\nrm ".$LOCK_FILE."\nrm ".$tempExecuteFile."\n[[ \$RESULT == 0 ]] && printf success >$RESULT_FILE || printf failure >$RESULT_FILE");
263 | shell_exec('/bin/bash '.$tempExecuteFile.' >>'.$STDOUT_FILE.' &');
264 | //usleep(100000); // So we can see some output on the first round.
265 | $output=htmlentities(file_get_contents($STDOUT_FILE),ENT_SUBSTITUTE);
266 | }
267 | echo json_encode(array("first"=>TRUE,"continue"=>TRUE,"output"=>$output
268 | ,"debug_tempExecuteFile"=>$tempExecuteFile,"debug_command"=>$COMMAND,"debug_stdout"=>$STDOUT_FILE
269 | ));
270 | }
271 | else{
272 | // Nothing to do.
273 | echo json_encode(array("idle"=>TRUE));
274 | }
275 | exit();
276 | }
277 |
278 | if(isset($_POST["rm"])){
279 | header('Content-Type: application/json');
280 | if(is_dir($PATH)&&!is_link($PATH))
281 | echo '{"success":'.(rmdir($PATH)===true?'true':'false').'}';
282 | else
283 | echo '{"success":'.(unlink($PATH)===true?'true':'false').'}';
284 | exit;
285 | }
286 | elseif(isset($_POST["cp"])){
287 | header('Content-Type: application/json');
288 | if(file_exists($_POST["dest"]))
289 | echo '{"success":false,"message":"File already exists in destination."}';
290 | else
291 | echo '{"success":'.(copy($_POST["source"],$_POST["dest"])===true?'true':'false').'}';
292 | exit;
293 | }
294 | elseif(isset($_POST["mv"])){
295 | header('Content-Type: application/json');
296 | if(file_exists($_POST["dest"]))
297 | echo '{"success":false,"message":"File already exists in destination."}';
298 | else
299 | echo '{"success":'.(rename($_POST["source"],$_POST["dest"])===true?'true':'false').'}';
300 | exit;
301 | }
302 | elseif(isset($_POST["content"])){
303 | header('Content-Type: application/json');
304 | $content=$_POST["content"];
305 | file_put_contents($PATH,$content);
306 | echo '{"msg":"Your changes have been saved."}';
307 | exit;
308 | }
309 | elseif(isset($_POST["new"])){
310 | header('Content-Type: application/json');
311 | $type=$_POST["type"];
312 | if(file_exists($PATH))
313 | //echo '{"success":false,"message":""}';
314 | echo json_encode(array("success"=>false,"message"=>"File already exists."));
315 | else{
316 | if($type==='dir'){
317 | $r=mkdir($PATH);
318 | }
319 | elseif($type==='file'){
320 | $r=file_put_contents($PATH,'');
321 | }
322 | //echo '{"success":'.($r!==false?'true':'false').'}';
323 | echo json_encode(array("success"=>$r!==false,"message"=>"Error writing ".$PATH));
324 | }
325 | exit;
326 | }
327 | elseif(isset($_GET["d"])){
328 | // http://php.net/manual/en/function.readfile.php
329 | header('Content-Description: File Transfer');
330 | $fi=finfo_open(FILEINFO_MIME);
331 | header('Content-Type: '.finfo_file($fi,$PATH));
332 | finfo_close($fi);
333 | header('Content-Disposition: inline; filename='.basename($PATH));
334 | header('Content-Transfer-Encoding: binary');
335 | header('Cache-Control: private, max-age=0, must-revalidate');
336 | header('Content-Length: '.filesize($PATH));
337 | ob_clean();
338 | flush();
339 | readfile($PATH);
340 | exit;
341 | }
342 | elseif(isset($_FILES['file'])){
343 | $dest=$PATH.'/'.$_FILES['file']['name'];
344 | $tempFile=$_FILES['file']['tmp_name'];
345 | header('Content-Type: application/json');
346 | echo json_encode(array("success"=>move_uploaded_file($_FILES['file']['tmp_name'],$dest)===true));
347 | exit;
348 | }
349 |
350 | $Recursive=FALSE;
351 | if(isset($_REQUEST["r"]))$Recursive=TRUE;
352 | $Grep="";
353 | if(isset($_REQUEST["grep"]))$Grep=$_REQUEST["grep"];
354 | $Find="";
355 | if(isset($_REQUEST["find"]))$Find=$_REQUEST["find"];
356 | $Locate="";
357 | if(isset($_REQUEST["locate"]))$Locate=$_REQUEST["locate"];
358 | $Title=substr($PATH,strrpos(str_replace('\\','/',$PATH),'/')+1);
359 | if($Title=='')$Title='Code Editor';
360 | ?>
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
414 | ";
426 | $html.="Save ";
427 | $html.="Save & Close ";
428 | $html.=" ";
429 | $c=file_get_contents($PATH);
430 | if(substr($c,0,3)==pack("CCC",0xef,0xbb,0xbf)) // Remove BOM
431 | $c=substr($c,3);
432 | $html.="" . htmlentities($c,ENT_SUBSTITUTE) . "
";
433 | return $html;
434 | }
435 | function getFiles(){
436 | global $PATH,$Recursive,$Locate,$DIRS_AT_TOP;
437 | if($Locate)
438 | $r=getFilesUsingLocate($PATH,$Locate);
439 | elseif($Recursive)
440 | $r=scandir_recursive($PATH);
441 | else
442 | $r=scandir($PATH);
443 | sort($r,SORT_STRING|SORT_FLAG_CASE);
444 |
445 | if($DIRS_AT_TOP && !$Locate && !$Recursive){
446 | $files=array();
447 | $dirs=array();
448 | foreach($r as $f)
449 | {
450 | if(is_dir($PATH.'/'.$f))
451 | array_push($dirs,$f);
452 | else
453 | array_push($files,$f);
454 | }
455 | $r=array_merge($dirs,$files);
456 | }
457 | return $r;
458 | }
459 | function renderFileList()
460 | {
461 | global $PATH,$Grep,$Find,$Recursive;
462 | $editorPPath=realpath($_SERVER["DOCUMENT_ROOT"]);
463 | $files=getFiles();
464 | $html="";
465 | $html.="";
466 | if ($PATH != ""){
467 | $PathEscaped=str_replace("'","\\'",$PATH);
468 | $delOnclick="onclick=\""."if(editor.del('$PathEscaped',null)===true){window.location=$('#list>.dir:first a:first').attr('href');}return(false)\"";
469 | if(get_dir_count($PATH)>0&&!is_link($PATH)) # Hide del button if dir not empty and not a symlink.
470 | $delOnclick='';
471 | $up=substr($PATH,0, strrpos($PATH,'/'));
472 | if($up==='')
473 | $up='/';
474 | elseif($Recursive)
475 | $up=$PATH; # Back out of search/recursive mode instead of up a level.
476 | $html.="
";
477 | }
478 | foreach($files as $filePath)
479 | {
480 | $rFile=$filePath;
481 | $aFile=($PATH=='/'?'':$PATH).'/'.$rFile;
482 | $pFile=realpath($aFile);
483 | if($rFile=='.'||$rFile=='..')
484 | continue;
485 | $isDir=is_dir($aFile);
486 |
487 |
488 | if($Find){
489 | if(preg_match('/'.$Find.'/i',$rFile)!==1)
490 | continue;
491 | }
492 | if($Grep!=''){
493 | if($isDir)continue;
494 | $fileContents=file_get_contents($aFile,false,null,0,5242880); // Limit to 5MB
495 | if(strpos(strtolower($fileContents),strtolower($Grep))===FALSE)
496 | continue;
497 | }
498 |
499 | if($isDir){
500 | $size=get_dir_count($aFile);
501 | $friendlySize=$size;
502 | }
503 | else{
504 | $size=filesize($aFile);
505 | $friendlySize=human_readable_filesize(filesize($aFile));
506 | }
507 | $age=human_readable_timespan(time()-filemtime($aFile));
508 | $direct='';
509 | if(strpos($pFile,$editorPPath)===0){
510 | $direct=urlencodelite(str_replace('\\','/',substr($pFile,strlen($editorPPath))));
511 | if($direct=='')$direct='/';
512 | }
513 | $aFileEscaped=str_replace("'","\\'",$aFile);
514 | if($isDir)
515 | $dlAnchor='';
516 | else
517 | $dlAnchor='
';
518 | $delOnclick="onclick=\"editor.del('$aFileEscaped',this);return(false)\"";
519 | if($isDir&&$size!==0&&!is_link($aFile))$delOnclick=''; # Hide del button if dir not empty and not a symlink.
520 |
521 | $segAs='';
522 |
523 | // Anchor each path segment.
524 | $segs=explode('/',$rFile);
525 | $segsCount=count($segs);
526 | $segAppend='';
527 | for($i=0;$i<$segsCount;$i++){
528 | $segAppend.='/'.$segs[$i];
529 | if($i===$segsCount-1 && ($size>=1048576 || preg_match('/\.(mp3|aac|ogg|wav|mid|jpg|bmp|gif|png|webp|webm|mp4|mkv|m4v|avi|pdf|zip|rar|tar|gz|7z)$/i',$rFile)===1))
530 | // Don't allow editing if file is over 1MB or is a media type.
531 | $href=$direct;
532 | else
533 | $href='?p='.urlencodelite(($PATH==='/'?'':$PATH).$segAppend);
534 | $segIsDir=$i!==$segsCount-1;
535 | $segAs.='
/ '.$segs[$i].' ';
536 | }
537 | $segAs=substr($segAs,28); // Trim leading "
/ "
538 |
539 | $pasteAnchor='';
540 | if($isDir)
541 | $pasteAnchor="
";
542 |
543 | $html.='
'.$segAs."
$friendlySize $age $dlAnchor$pasteAnchor
";
544 | }
545 | $html.="
";
546 | return $html;
547 | }
548 | if(isset($BRANDING_FOOTER))echo $BRANDING_FOOTER;
549 | ?>
550 |
551 | $(function(){$(".shellButton").triggerHandler("click");});'; // If was postback then shell form is already open so need to trigger click to init form events ?>
552 |
553 |
554 |
--------------------------------------------------------------------------------