Textpattern CMS support forum
You are not logged in. Register | Login | Help
- Topics: Active | Unanswered
Pages: 1
#1 2024-10-14 14:31:38
- Adams
- Plugin Author
- Registered: 2024-08-30
- Posts: 38
text file editor
Hello,
Is there any plugin to add, edit, and list text files?
I tried to get this plug in (but it does not exit anymore).
https://textpattern.org/plugins/1256/spf_ext
The files “on admin page” is very good; the user can upload and delete, but there is no option to edit a file.
Thank you
Thank you
Offline
#2 2024-10-28 17:28:50
- Adams
- Plugin Author
- Registered: 2024-08-30
- Posts: 38
Re: text file editor
Hello,
I had to develop my own small plugin as a file manager, just basic, not too fancy, just to navigate folders and edit any text or PHP files (that is what I need so far). I can post it here if you guys allow that.
Offline
Re: text file editor
Definitely! If you’re willing to share it, that would be great. Usually plugin authors prefix their plugin with a three letter initial by way of an identifier.
TXP Builders – finely-crafted code, design and txp
Online
Re: text file editor
Ooh yes, absolutely please. Peruse the list of registered prefixes and choose a different one that’s not already in use. We’ll get it added to that article so it’s your prefix from now on.
Use it in all global function/class names and language strings – basically anything that is in the global scope – to avoid clashes with the core system or other plugins.
Once your plugin is polished, let us have the URL where it can be downloaded (e.g. GitHub) and we’ll also get it added to the plugin repository which we need to knuckle down and populate more fully now 4.9.0 is almost here.
The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.
Txp Builders – finely-crafted code, design and Txp
Offline
#5 2024-10-29 12:35:45
- Adams
- Plugin Author
- Registered: 2024-08-30
- Posts: 38
Re: text file editor
Here you go:
Feel free to do whatever you want to do with it, just basic, simple, all coded copied from another file manager php script, with minor modifications to make it work as a plugin.
Sorry, not cleaned up, has some extra leftover codes, but whoever has time can play with it and make it more usable and real file manager; this is just starting and works for me for now.
<?php
echo '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" crossorigin="anonymous"> ';
/*
Author: Adam Allen
Version: 1.0
This plugin done by help from this open source filemanager
https://github.com/alexantr/filemanager
*/
if (txpinterface === 'admin')
{
new adm_fman();
}
$use_highlightjs = true;
// highlight.js style
$highlightjs_style = 'vs';
$default_timezone = 'America/Chicago';
$root_path = $_SERVER['DOCUMENT_ROOT'] ;
$root_url = '?event=adm_fman';
$http_host = $_SERVER['HTTP_HOST'];
$iconv_input_encoding = 'CP1251';
$datetime_format = 'd.m.y H:i';
$is_https = '?event=adm_fman';
$p = isset($_GET['p']) ? $_GET['p'] : (isset($_POST['p']) ? $_POST['p'] : '');
// clean path
$p = fm_clean_path($p);
// instead globals vars
define('FM_PATH', $p);
defined('FM_ICONV_INPUT_ENC') || define('FM_ICONV_INPUT_ENC', $iconv_input_encoding);
defined('FM_USE_HIGHLIGHTJS') || define('FM_USE_HIGHLIGHTJS', $use_highlightjs);
defined('FM_HIGHLIGHTJS_STYLE') || define('FM_HIGHLIGHTJS_STYLE', $highlightjs_style);
defined('FM_DATETIME_FORMAT') || define('FM_DATETIME_FORMAT', $datetime_format);
$root_url = fm_clean_path($root_url);
// abs path for site
defined('FM_ROOT_PATH') || define('FM_ROOT_PATH', $root_path);
defined('FM_ROOT_URL') || define('FM_ROOT_URL','?event=adm_fman' );
defined('FM_SELF_URL') || define('FM_SELF_URL', '?event=adm_fman');
//'Location: ?event=prefs#prefs_group_com_article_image'
define('FM_IS_WIN', DIRECTORY_SEPARATOR == '\\');
////////////////////////////////////////////////////////////////////////
function fm_get_text_names()
{
return array(
'license',
'readme',
'authors',
'contributors',
'changelog',
);
}
function fm_get_image_exts()
{
return array('ico', 'gif', 'jpg', 'jpeg', 'jpc', 'jp2', 'jpx', 'xbm', 'wbmp', 'png', 'bmp', 'tif', 'tiff', 'psd');
}
function fm_is_utf8($string)
{
return preg_match('//u', $string);
}
function fm_get_mime_type($file_path)
{
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file_path);
finfo_close($finfo);
return $mime;
} elseif (function_exists('mime_content_type')) {
return mime_content_type($file_path);
} elseif (!stristr(ini_get('disable_functions'), 'shell_exec')) {
$file = escapeshellarg($file_path);
$mime = shell_exec('file -bi ' . $file);
return $mime;
} else {
return '--';
}
}
function fm_get_video_exts()
{
return array('webm', 'mp4', 'm4v', 'ogm', 'ogv', 'mov');
}
function fm_get_audio_exts()
{
return array('wav', 'mp3', 'ogg', 'm4a');
}
function fm_get_text_exts()
{
return array(
'txt', 'css', 'ini', 'conf', 'log', 'htaccess', 'passwd', 'ftpquota', 'sql', 'js', 'json', 'sh', 'config',
'php', 'php4', 'php5', 'phps', 'phtml', 'htm', 'html', 'shtml', 'xhtml', 'xml', 'xsl', 'm3u', 'm3u8', 'pls', 'cue',
'eml', 'msg', 'csv', 'bat', 'twig', 'tpl', 'md', 'gitignore', 'less', 'sass', 'scss', 'c', 'cpp', 'cs', 'py',
'map', 'lock', 'dtd', 'svg',
);
}
function fm_get_zif_info($path)
{
if (function_exists('zip_open')) {
$arch = zip_open($path);
if ($arch) {
$filenames = array();
while ($zip_entry = zip_read($arch)) {
$zip_name = zip_entry_name($zip_entry);
$zip_folder = substr($zip_name, -1) == '/';
$filenames[] = array(
'name' => $zip_name,
'filesize' => zip_entry_filesize($zip_entry),
'compressed_size' => zip_entry_compressedsize($zip_entry),
'folder' => $zip_folder
//'compression_method' => zip_entry_compressionmethod($zip_entry),
);
}
zip_close($arch);
return $filenames;
}
}
return false;
}
function fm_convert_win($filename)
{
if (FM_IS_WIN && function_exists('iconv')) {
$filename = iconv(FM_ICONV_INPUT_ENC, 'UTF-8//IGNORE', $filename);
}
return $filename;
}
function fm_redirect($url, $code = 302)
{
header('Location: ' . '?event=adm_fman', true, $code);
exit;
}
function fm_set_msg($msg, $status = 'ok')
{
$_SESSION['message'] = $msg;
$_SESSION['status'] = $status;
}
function fm_enc($text)
{
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}
function fm_clean_path($path)
{
$path = trim($path);
$path = trim($path, '\\/');
$path = str_replace(array('../', '..\\'), '', $path);
if ($path == '..') {
$path = '';
}
return str_replace('\\', '/', $path);
}
function fm_get_file_icon_class($path)
{
// get extension
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
switch ($ext) {
case 'ico': case 'gif': case 'jpg': case 'jpeg': case 'jpc': case 'jp2':
case 'jpx': case 'xbm': case 'wbmp': case 'png': case 'bmp': case 'tif':
case 'tiff':
$img = 'icon-file_image';
break;
case 'txt': case 'css': case 'ini': case 'conf': case 'log': case 'htaccess':
case 'passwd': case 'ftpquota': case 'sql': case 'js': case 'json': case 'sh':
case 'config': case 'twig': case 'tpl': case 'md': case 'gitignore':
case 'less': case 'sass': case 'scss': case 'c': case 'cpp': case 'cs': case 'py':
case 'map': case 'lock': case 'dtd':
$img = 'icon-file_text';
break;
case 'zip': case 'rar': case 'gz': case 'tar': case '7z':
$img = 'icon-file_zip';
break;
case 'php': case 'php4': case 'php5': case 'phps': case 'phtml':
$img = 'icon-file_php';
break;
case 'htm': case 'html': case 'shtml': case 'xhtml':
$img = 'icon-file_html';
break;
case 'xml': case 'xsl': case 'svg':
$img = 'icon-file_code';
break;
case 'wav': case 'mp3': case 'mp2': case 'm4a': case 'aac': case 'ogg':
case 'oga': case 'wma': case 'mka': case 'flac': case 'ac3': case 'tds':
$img = 'icon-file_music';
break;
case 'm3u': case 'm3u8': case 'pls': case 'cue':
$img = 'icon-file_playlist';
break;
case 'avi': case 'mpg': case 'mpeg': case 'mp4': case 'm4v': case 'flv':
case 'f4v': case 'ogm': case 'ogv': case 'mov': case 'mkv': case '3gp':
case 'asf': case 'wmv':
$img = 'icon-file_film';
break;
case 'eml': case 'msg':
$img = 'icon-file_outlook';
break;
case 'xls': case 'xlsx':
$img = 'icon-file_excel';
break;
case 'csv':
$img = 'icon-file_csv';
break;
case 'doc': case 'docx':
$img = 'icon-file_word';
break;
case 'ppt': case 'pptx':
$img = 'icon-file_powerpoint';
break;
case 'ttf': case 'ttc': case 'otf': case 'woff':case 'woff2': case 'eot': case 'fon':
$img = 'icon-file_font';
break;
case 'pdf':
$img = 'icon-file_pdf';
break;
case 'psd':
$img = 'icon-file_photoshop';
break;
case 'ai': case 'eps':
$img = 'icon-file_illustrator';
break;
case 'fla':
$img = 'icon-file_flash';
break;
case 'swf':
$img = 'icon-file_swf';
break;
case 'exe': case 'msi':
$img = 'icon-file_application';
break;
case 'bat':
$img = 'icon-file_terminal';
break;
default:
$img = 'icon-document';
}
return $img;
}
function fm_get_filesize($size)
{
if ($size < 1000) {
return sprintf('%s B', $size);
} elseif (($size / 1024) < 1000) {
return sprintf('%s KiB', round(($size / 1024), 2));
} elseif (($size / 1024 / 1024) < 1000) {
return sprintf('%s MiB', round(($size / 1024 / 1024), 2));
} elseif (($size / 1024 / 1024 / 1024) < 1000) {
return sprintf('%s GiB', round(($size / 1024 / 1024 / 1024), 2));
} else {
return sprintf('%s TiB', round(($size / 1024 / 1024 / 1024 / 1024), 2));
}
}
function fm_get_parent_path($path)
{
$path = fm_clean_path($path);
if ($path != '') {
$array = explode('/', $path);
if (count($array) > 1) {
$array = array_slice($array, 0, -1);
return implode('/', $array);
}
return '';
}
return false;
}
class adm_fman
{
protected $event = __CLASS__;
public function __construct()
{
add_privs($this->event, '1'); // Publishers only
register_tab('extensions', $this->event, 'File Manager');
register_callback(array($this, 'hw_panel'), $this->event);
}
/**
* Plugin dispatcher: handle steps.
*/
public function hw_panel($evt, $stp)
{
$available_steps = array(
'edit' => false,
'view' => false,
'zip' => false,
'list' => true,
'save' => false, // Should use 'true' in real code
);
if (!$stp or !bouncer($stp, $available_steps)) {
$stp = 'list';
}
$this->$stp();
}
/**
* Display the user interface with some hyperlink actions.
*/
public function zip()
{
// pagetop('');
$path = FM_ROOT_PATH;
if (FM_PATH != '') {
$path .= '/' . FM_PATH;
}
if (class_exists('ZipArchive'))
{
$zip = new ZipArchive();
$zip_filename = 'download_' . date('Y-m-d_H-i-s') . '.tar';
$zip_filepath = sys_get_temp_dir() . '/' . $zip_filename;
if ($zip->open($zip_filepath, ZipArchive::CREATE) !== TRUE)
{
echo('Cannot create ZIP file');
// $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' . urlencode($FM_PATH));
}
$zip->addFile($path, basename($path));
$zip->close();
}
echo $path . '<br/>';
}
public function list()
{
pagetop('');
echo '<p class="path"><a href="?event=adm_fman"><i class="fa fa-home"></i> Home </a>';
// echo 'File manager start from here';
if (!isset($_GET['p']))
{
// fm_redirect('?event=adm_fman&p=');
// echo FM_SELF_URL . '&p=';
}
$path = FM_ROOT_PATH;
if (FM_PATH != '') {
$path .= '/' . FM_PATH;
}
// check path
if (!is_dir($path)) {
fm_redirect(FM_SELF_URL . '&p=');
}
$parent = fm_get_parent_path(FM_PATH);
$objects = is_readable($path) ? scandir($path) : array();
$folders = array();
$files = array();
$num_files = count($files);
$num_folders = count($folders);
$all_files_size = 0;
if (is_array($objects)) {
foreach ($objects as $file) {
if ($file == '.' || $file == '..') {
continue;
}
$new_path = $path . '/' . $file;
if (is_file($new_path)) {
$files[] = $file;
} elseif (is_dir($new_path) && $file != '.' && $file != '..') {
$folders[] = $file;
}
}
}
if (!empty($files)) {
natcasesort($files);
}
if (!empty($folders)) {
natcasesort($folders);
}
$myform =
'<form action="" method="post">
<input type="hidden" name="p" value="'. fm_enc(FM_PATH) .'">
<input type="hidden" name="group" value="1">
<table><tr>
<th style="width:3%"><label><input type="checkbox" title="Invert selection" onclick="checkbox_toggle()"></label></th>
<th>Name</th><th style="width:10%">Size</th>
<th style="width:12%">Modified</th>';
if (!FM_IS_WIN):
$myform .='<th style="width:6%">Perms</th><th style="width:10%">Owner</th>';
endif;
$myform .='<th style="width:13%"></th>' .
'<th style="width:10%; float:left">Action</th> ' .
'</tr>';
foreach ($folders as $f)
{
$is_link = is_link($path . '/' . $f);
$img = $is_link ? 'icon-link_folder' : 'icon-folder';
$modif = date(FM_DATETIME_FORMAT, filemtime($path . '/' . $f));
$perms = substr(decoct(fileperms($path . '/' . $f)), -4);
if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) {
$owner = posix_getpwuid(fileowner($path . '/' . $f));
$group = posix_getgrgid(filegroup($path . '/' . $f));
} else {
$owner = array('name' => '?');
$group = array('name' => '?');
}
//echo fm_enc($f);
$myform .='<tr>
<td><label><input type="checkbox" name="file[]" value="' . fm_enc($f) .'"></label></td>
<td> <i class="fa fa-folder-o" style="float:left; padding-right: 10px; color:orange"></i><div class="filename"><a href="?event=adm_fman&p=' . urlencode(trim(FM_PATH . '/' . $f, '/')) .'"><i class="' . $img .'"></i> ' . fm_enc(fm_convert_win($f)) .'</a>' . ($is_link ? ' → <i>' . fm_enc(readlink($path . '/' . $f)) . '</i>' : '') . '</div></td>
<td>Folder</td><td>' . $modif .'</td>';
if (!FM_IS_WIN):
//$myform .='<td>' . $perms .'</td><td>' . fm_enc($owner['name'] . ':' . $group['name']) .'</td>';
$myform .='<td><a title="Change Permissions" href="?event=adm_fman&p='. urlencode(FM_PATH) .'&chmod='. urlencode($f) .'">'. $perms .'</a></td>';
endif;
$myform .='<td>
<a title="Delete" href="?event=adm_fman&p=' . urlencode(FM_PATH) .'&del=' . urlencode($f) .'" onclick="return confirm("Delete folder?");"><i class="icon-cross"></i></a>
<a title="Rename" href="#" onclick="rename("' . fm_enc(FM_PATH) .'", "' . fm_enc($f) .'");return false;"><i class="icon-rename"></i></a>
<a title="Copy to..." href="?event=adm_fman&p=&copy=' . urlencode(trim(FM_PATH . '/' . $f, '/')) .'"><i class="icon-copy"></i></a>
<a title="Direct link" href="' . fm_enc(FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $f . '/') .'" target="_blank"><i class="icon-chain"></i></a>
</td></tr>';
flush();
}
foreach ($files as $f) {
$is_link = is_link($path . '/' . $f);
$img = $is_link ? 'icon-link_file' : fm_get_file_icon_class($path . '/' . $f);
$modif = date(FM_DATETIME_FORMAT, filemtime($path . '/' . $f));
$filesize_raw = filesize($path . '/' . $f);
$filesize = fm_get_filesize($filesize_raw);
$filelink = '?event=adm_fman&step=view&p=' . urlencode(FM_PATH) . '&view=' . urlencode($f);
$all_files_size += $filesize_raw;
$perms = substr(decoct(fileperms($path . '/' . $f)), -4);
if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) {
$owner = posix_getpwuid(fileowner($path . '/' . $f));
$group = posix_getgrgid(filegroup($path . '/' . $f));
} else {
$owner = array('name' => '?');
$group = array('name' => '?');
}
$myform .='<tr>
<td><label><input type="checkbox" name="file[]" value="'. fm_enc($f) .'"></label></td>
<td><i class="fa fa-file-code-o" style="float:left; padding-right: 10px; color:red" aria-hidden="true"></i><div class="filename"><a href="'. fm_enc($filelink) .'" title="File info"><i class="'. $img .'"></i> '. fm_enc(fm_convert_win($f)) .'</a>'. ($is_link ? ' → <i>' . fm_enc(readlink($path . '/' . $f)) . '</i>' : '') .'</div></td>
<td><span class="gray" title="'. printf('%s bytes', $filesize_raw) .'">'. $filesize .'</span></td>
<td>'. $modif .'</td>';
if (!FM_IS_WIN):
$myform .='<td><a title="Change Permissions" href="?event=adm_fman&p='. urlencode(FM_PATH) .'&chmod='. urlencode($f) .'">'. $perms .'</a></td>
<td>'. fm_enc($owner['name'] . ':' . $group['name']) .'</td>';
endif;
$myform .='<td>
<a href="?event=adm_fman&step=edit&p=' . urlencode(FM_PATH) . '&view=' . urlencode($f).'">
<i class="fa fa-pencil-square-o" style="float:left; padding-right: 10px; color:red">Edit</i>
</a></td>
<a title="Copy to..." href="?event=adm_fman&p='. urlencode(FM_PATH) .'&copy='. urlencode(trim(FM_PATH . '/' . $f, '/')) .'"><i class="icon-copy"></i></a>
<a title="Direct link" href="'. fm_enc(FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $f) .'" target="_blank"><i class="icon-chain"></i></a>
<a title="Download" href="?event=adm_fman&p='. urlencode(FM_PATH) .'&dl='. urlencode($f) .'"><i class="icon-download"></i></a>
</td></tr>';
flush();
}
echo $myform;
if (empty($folders) && empty($files))
{
$myform2='<tr><td></td><td colspan="'. !FM_IS_WIN ? "6" : "4" .'"><em>Folder is empty</em></td></tr>';
}
else
{
$myform2='<tr><td class="gray"></td><td class="gray" colspan="'. !FM_IS_WIN ? "6" : "4" .'">
Full size: <span title="'. printf("%s bytes", $all_files_size) .'">'. fm_get_filesize($all_files_size) .
'</span>,files: '. $num_files .', folders: '. $num_folders .'</td></tr>';
}
$myform2='</table>
<br/>
<a href="?event=adm_fman&step=zip&p='. urlencode(FM_PATH) . '"><i class="fa fa-file-zip-o" style="font-size:24px; color:brown; padding:5px"></i> Zip</a></p>
</form>';
echo $myform2;
}
/**
* Perform a save action.
*/
public function save()
{
if (empty($_POST['filetxt']) )
{
exit;
}
// echo 'Save step triggered';
pagetop(); //show main menu
$path = FM_ROOT_PATH;
if (FM_PATH != '') {
$path .= '/' . FM_PATH;
}
// echo 'Edit step triggered';
$file = $_GET['view'];
$file = fm_clean_path($file);
$file = str_replace('/', '', $file);
if ($file == '' || !is_file($path . '/' . $file)) {
fm_set_msg('File not found', 'error');
fm_redirect(FM_SELF_URL . '?event=adm_fman&p=' . urlencode(FM_PATH));
}
$file_path = $path . '/' . $file;
//echo $file_path;
$current= $_POST['filetxt'];
file_put_contents($file_path, $current);
$this->list();
}
/***************************************************** edit *****************************/
public function edit()
{
pagetop(); //show main menu
$path = FM_ROOT_PATH;
if (FM_PATH != '') {
$path .= '/' . FM_PATH;
}
// echo 'Edit step triggered';
$file = $_GET['view'];
$file = fm_clean_path($file);
$file = str_replace('/', '', $file);
if ($file == '' || !is_file($path . '/' . $file)) {
fm_set_msg('File not found', 'error');
fm_redirect(FM_SELF_URL . '?event=adm_fman&p=' . urlencode(FM_PATH));
}
// fm_show_header(); // HEADER
// fm_show_nav_path(FM_PATH); // current path
$file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file);
$file_path = $path . '/' . $file;
// header("Content-type: text/plain");
// header("Content-Disposition: attachment; filename='.$file_path.'");
$ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
$mime_type = fm_get_mime_type($file_path);
$filesize = filesize($file_path);
$is_zip = false;
$is_image = false;
$is_audio = false;
$is_video = false;
$is_text = false;
$view_title = 'File';
$filenames = false; // for zip
$content = ''; // for text
if ($ext == 'zip') {
$is_zip = true;
$view_title = 'Archive';
$filenames = fm_get_zif_info($file_path);
} elseif (in_array($ext, fm_get_image_exts())) {
$is_image = true;
$view_title = 'Image';
} elseif (in_array($ext, fm_get_audio_exts())) {
$is_audio = true;
$view_title = 'Audio';
} elseif (in_array($ext, fm_get_video_exts())) {
$is_video = true;
$view_title = 'Video';
} elseif (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0, 4) == 'text' || in_array($mime_type, fm_get_text_mimes())) {
$is_text = true;
$content = file_get_contents($file_path);
}
echo'<form class="txp-edit" id="section_details" method="post" action="?event=adm_fman&step=save&p=' . urlencode(FM_PATH) . '&view=' . urlencode($file). '">';
echo ' <textarea name="filetxt" id="filetxt" class="form-control" rows="10" style="overflow-y:scroll; resize: none;height:300px;" >' ;
echo htmlspecialchars($content);
echo '</textarea> <br/>';
echo '<input name="savebtn" id="savebtn" type="submit" value="Save">';
echo '</form>';
exit;
$theview='<div class="path">
<p class="break-word"><b>'. $view_title .' "'. fm_enc(fm_convert_win($file)) .'"</b></p>
<p class="break-word">
Full path: '. fm_enc(fm_convert_win($file_path)) .'<br>
File size: '. fm_get_filesize($filesize) ;
if ($filesize >= 1000):
$theview .='('. sprintf("%s bytes", $filesize) .')';
endif;
$theview .='<br>
MIME-type: '. $mime_type .'<br>';
// ZIP info
if ($is_zip && $filenames !== false) {
$total_files = 0;
$total_comp = 0;
$total_uncomp = 0;
foreach ($filenames as $fn) {
if (!$fn['folder']) {
$total_files++;
}
$total_comp += $fn['compressed_size'];
$total_uncomp += $fn['filesize'];
}
$theview.='
Files in archive: '. $total_files .'<br>
Total size: '. fm_get_filesize($total_uncomp) .'<br>
Size in archive: '. fm_get_filesize($total_comp) .'<br>
Compression: '. round(($total_comp / $total_uncomp) * 100) .'%<br>';
}
// Image info
if ($is_image) {
$image_size = getimagesize($file_path);
echo 'Image sizes: ' . (isset($image_size[0]) ? $image_size[0] : "0") . ' x ' . (isset($image_size[1]) ? $image_size[1] : "0") . '<br>';
}
// Text info
if ($is_text) {
$is_utf8 = fm_is_utf8($content);
if (function_exists('iconv')) {
if (!$is_utf8) {
$content = iconv(FM_ICONV_INPUT_ENC, 'UTF-8//IGNORE', $content);
}
}
echo 'Charset: ' . ($is_utf8 ? "utf-8" : "8 bit") . '<br>';
}
$theview.='
</p>
<p>
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&dl='. urlencode($file) .'"><i class="icon-download"></i> Download</a></b>
<b><a href="'. fm_enc($file_url) .'" target="_blank"><i class="icon-chain"></i> Open</a></b>
';
// ZIP actions
if ($is_zip && $filenames !== false)
{
$zip_name = pathinfo($file_path, PATHINFO_FILENAME);
$theview.='
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&unzip='. urlencode($file) .'"><i class="icon-apply"></i> Unpack</a></b>
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&unzip='. urlencode($file) .'&tofolder=1" title="Unpack to '. fm_enc($zip_name) .'"><i class="icon-apply"></i>
Unpack to folder</a></b> ';
}
$theview.='
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'"><i class="icon-goback"></i> Back</a></b>
</p>';
if ($is_zip) {
// ZIP content
if ($filenames !== false) {
echo '<code class="maxheight">';
foreach ($filenames as $fn) {
if ($fn['folder']) {
echo '<b>' . fm_enc($fn['name']) . '</b><br>';
} else {
echo $fn['name'] . ' (' . fm_get_filesize($fn["filesize"]) . ')<br>';
}
}
echo '</code>';
} else {
echo '<p>Error while fetching archive info</p>';
}
} elseif ($is_image) {
// Image content
if (in_array($ext, array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'ico'))) {
echo '<p><img src="' . fm_enc($file_url) . '" alt="" class="preview-img"></p>';
}
} elseif ($is_audio) {
// Audio content
echo '<p><audio src="' . fm_enc($file_url) . '" controls preload="metadata"></audio></p>';
} elseif ($is_video) {
// Video content
echo '<div class="preview-video"><video src="' . fm_enc($file_url) . '" width="640" height="360" controls preload="metadata"></video></div>';
} elseif ($is_text) {
if (FM_USE_HIGHLIGHTJS) {
// highlight
$hljs_classes = array(
'shtml' => 'xml',
'htaccess' => 'apache',
'phtml' => 'php',
'lock' => 'json',
'svg' => 'xml',
);
$hljs_class = isset($hljs_classes[$ext]) ? 'lang-' . $hljs_classes[$ext] : 'lang-' . $ext;
if (empty($ext) || in_array(strtolower($file), fm_get_text_names()) || preg_match('#\.min\.(css|js)$#i', $file)) {
$hljs_class = 'nohighlight';
}
$content = '<pre class="with-hljs"><code class="' . $hljs_class . '">' . fm_enc($content) . '</code></pre>';
} elseif (in_array($ext, array('php', 'php4', 'php5', 'phtml', 'phps'))) {
// php highlight
$content = highlight_string($content, true);
} else {
$content = '<pre>' . fm_enc($content) . '</pre>';
}
echo $content;
}
$theview.='
</div>';
echo $theview;
exit;
// $this->list();
}
/***************************************************** view *****************************/
public function view()
{
pagetop(); //show main menu
$path = FM_ROOT_PATH;
if (FM_PATH != '') {
$path .= '/' . FM_PATH;
}
// echo 'Edit step triggered';
$file = $_GET['view'];
$file = fm_clean_path($file);
$file = str_replace('/', '', $file);
if ($file == '' || !is_file($path . '/' . $file)) {
fm_set_msg('File not found', 'error');
fm_redirect(FM_SELF_URL . '?event=adm_fman&p=' . urlencode(FM_PATH));
}
// fm_show_header(); // HEADER
// fm_show_nav_path(FM_PATH); // current path
$file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file);
$file_path = $path . '/' . $file;
$ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
$mime_type = fm_get_mime_type($file_path);
$filesize = filesize($file_path);
$is_zip = false;
$is_image = false;
$is_audio = false;
$is_video = false;
$is_text = false;
$view_title = 'File';
$filenames = false; // for zip
$content = ''; // for text
if ($ext == 'zip') {
$is_zip = true;
$view_title = 'Archive';
$filenames = fm_get_zif_info($file_path);
} elseif (in_array($ext, fm_get_image_exts())) {
$is_image = true;
$view_title = 'Image';
} elseif (in_array($ext, fm_get_audio_exts())) {
$is_audio = true;
$view_title = 'Audio';
} elseif (in_array($ext, fm_get_video_exts())) {
$is_video = true;
$view_title = 'Video';
} elseif (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0, 4) == 'text' || in_array($mime_type, fm_get_text_mimes())) {
$is_text = true;
$content = file_get_contents($file_path);
}
$theview='<div class="path">
<p class="break-word"><b>'. $view_title .' "'. fm_enc(fm_convert_win($file)) .'"</b></p>
<p class="break-word">
Full path: '. fm_enc(fm_convert_win($file_path)) .'<br>
File size: '. fm_get_filesize($filesize) ;
if ($filesize >= 1000):
$theview .='('. sprintf("%s bytes", $filesize) .')';
endif;
$theview .='<br>
MIME-type: '. $mime_type .'<br>';
// ZIP info
if ($is_zip && $filenames !== false) {
$total_files = 0;
$total_comp = 0;
$total_uncomp = 0;
foreach ($filenames as $fn) {
if (!$fn['folder']) {
$total_files++;
}
$total_comp += $fn['compressed_size'];
$total_uncomp += $fn['filesize'];
}
$theview.='
Files in archive: '. $total_files .'<br>
Total size: '. fm_get_filesize($total_uncomp) .'<br>
Size in archive: '. fm_get_filesize($total_comp) .'<br>
Compression: '. round(($total_comp / $total_uncomp) * 100) .'%<br>';
}
// Image info
if ($is_image) {
$image_size = getimagesize($file_path);
echo 'Image sizes: ' . (isset($image_size[0]) ? $image_size[0] : "0") . ' x ' . (isset($image_size[1]) ? $image_size[1] : "0") . '<br>';
}
// Text info
if ($is_text) {
$is_utf8 = fm_is_utf8($content);
if (function_exists('iconv')) {
if (!$is_utf8) {
$content = iconv(FM_ICONV_INPUT_ENC, 'UTF-8//IGNORE', $content);
}
}
echo 'Charset: ' . ($is_utf8 ? "utf-8" : "8 bit") . '<br>';
}
$theview.='
</p>
<p>
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&dl='. urlencode($file) .'"><i class="icon-download"></i> Download</a></b>
<b><a href="'. fm_enc($file_url) .'" target="_blank"><i class="icon-chain"></i> Open</a></b>
';
// ZIP actions
if ($is_zip && $filenames !== false)
{
$zip_name = pathinfo($file_path, PATHINFO_FILENAME);
$theview.='
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&unzip='. urlencode($file) .'"><i class="icon-apply"></i> Unpack</a></b>
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&unzip='. urlencode($file) .'&tofolder=1" title="Unpack to '. fm_enc($zip_name) .'"><i class="icon-apply"></i>
Unpack to folder</a></b> ';
}
$theview.='
<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'"><i class="icon-goback"></i> Back</a></b>
</p>';
if ($is_zip) {
// ZIP content
if ($filenames !== false) {
echo '<code class="maxheight">';
foreach ($filenames as $fn) {
if ($fn['folder']) {
echo '<b>' . fm_enc($fn['name']) . '</b><br>';
} else {
echo $fn['name'] . ' (' . fm_get_filesize($fn["filesize"]) . ')<br>';
}
}
echo '</code>';
} else {
echo '<p>Error while fetching archive info</p>';
}
} elseif ($is_image) {
// Image content
if (in_array($ext, array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'ico'))) {
echo '<p><img src="' . fm_enc($file_url) . '" alt="" class="preview-img"></p>';
}
} elseif ($is_audio) {
// Audio content
echo '<p><audio src="' . fm_enc($file_url) . '" controls preload="metadata"></audio></p>';
} elseif ($is_video) {
// Video content
echo '<div class="preview-video"><video src="' . fm_enc($file_url) . '" width="640" height="360" controls preload="metadata"></video></div>';
} elseif ($is_text) {
if (FM_USE_HIGHLIGHTJS) {
// highlight
$hljs_classes = array(
'shtml' => 'xml',
'htaccess' => 'apache',
'phtml' => 'php',
'lock' => 'json',
'svg' => 'xml',
);
$hljs_class = isset($hljs_classes[$ext]) ? 'lang-' . $hljs_classes[$ext] : 'lang-' . $ext;
if (empty($ext) || in_array(strtolower($file), fm_get_text_names()) || preg_match('#\.min\.(css|js)$#i', $file)) {
$hljs_class = 'nohighlight';
}
$content = '<pre class="with-hljs"><code class="' . $hljs_class . '">' . fm_enc($content) . '</code></pre>';
} elseif (in_array($ext, array('php', 'php4', 'php5', 'phtml', 'phps'))) {
// php highlight
$content = highlight_string($content, true);
} else {
$content = '<pre>' . fm_enc($content) . '</pre>';
}
echo $content;
}
$theview.='
</div>';
//fm_show_footer();
echo $theview;
exit;
// $this->list();
}
}
/**
* Class to work with zip files (using ZipArchive)
*/
class FM_Zipper
{
private $zip;
public function __construct()
{
$this->zip = new ZipArchive();
}
/**
* Create archive with name $filename and files $files (RELATIVE PATHS!)
* @param string $filename
* @param array|string $files
* @return bool
*/
public function create($filename, $files)
{
$res = $this->zip->open($filename, ZipArchive::CREATE);
if ($res !== true) {
return false;
}
if (is_array($files)) {
foreach ($files as $f) {
if (!$this->addFileOrDir($f)) {
$this->zip->close();
return false;
}
}
$this->zip->close();
return true;
} else {
if ($this->addFileOrDir($files)) {
$this->zip->close();
return true;
}
return false;
}
}
/**
* Extract archive $filename to folder $path (RELATIVE OR ABSOLUTE PATHS)
* @param string $filename
* @param string $path
* @return bool
*/
public function unzip($filename, $path)
{
$res = $this->zip->open($filename);
if ($res !== true) {
return false;
}
if ($this->zip->extractTo($path)) {
$this->zip->close();
return true;
}
return false;
}
/**
* Add file/folder to archive
* @param string $filename
* @return bool
*/
private function addFileOrDir($filename)
{
if (is_file($filename)) {
return $this->zip->addFile($filename);
} elseif (is_dir($filename)) {
return $this->addDir($filename);
}
return false;
}
/**
* Add folder recursively
* @param string $path
* @return bool
*/
private function addDir($path)
{
if (!$this->zip->addEmptyDir($path)) {
return false;
}
$objects = scandir($path);
if (is_array($objects)) {
foreach ($objects as $file) {
if ($file != '.' && $file != '..') {
if (is_dir($path . '/' . $file)) {
if (!$this->addDir($path . '/' . $file)) {
return false;
}
} elseif (is_file($path . '/' . $file)) {
if (!$this->zip->addFile($path . '/' . $file)) {
return false;
}
}
}
}
return true;
}
return false;
}
}
Offline
Re: text file editor
Hi Adam,
Thanks for posting your plugin. I tried it out and it works fine for editing text files but as you say, in the current form it’s only suitable for personal use by admins who know what they are doing. As it allows you to edit the source files, it would be a security risk if used maliciously or break your site if used inexpertly.
That said, to use it, try it out, save the above code to a file with a .php extension, e.g. adm_fman.php
and place these in a your plugin development folder, e.g. /textpattern/plugin_dev/
. Make sure you set the matching folder path in Admin › Preferences › Admin. The next time you load the admin area, you’ll see an option File Manager in the Extensions tab.
This is the basic file manager pane.
This is what you see if you click on the name of a text file (e.g. the “read” action):
and this is the edit pane when you click on the “edit” button:
I could then successfully edit text files on my local installation.
—-
Things to note (not criticisms, just a note on the state of things, should other people want to try it):
- I had no luck with the “zip” action. It returns a “folder” error. I presume that’s for use with the checkboxes in the first column. I may be lacking something in my local php installation.
- There’s an edit link for images, which shows a blank edit textarea.
- I didn’t see any code highlighting, presumably as highlightjs is lacking (FYI: Textpattern has Prism already bundled).
- It’s not yet set up as a txp plugin, hence the development directory method.
- Because it provides direct access to the server, the original GitHub repo warns “Do not use this script as a regular file manager in public area. After all actions you must delete this script from the server.” In this case the script is loaded on the admin-side and is only accessible to users with the “Publisher” role, but it’s probably worth making the plugin_dev directory not publicly accessible via the browser.
Thanks again for posting your code.
TXP Builders – finely-crafted code, design and txp
Online
#7 2024-11-03 15:50:02
- Adams
- Plugin Author
- Registered: 2024-08-30
- Posts: 38
Re: text file editor
Yes, just a simple file manager for admins only (this will save a lot of time; I edit some files frequently). Yup, it looks like the zip function is not working if the extension is not enabled. Here is the replacement as tar instead of zip.
Replace public function zip()
public function zip()
{
// pagetop('');
$path = FM_ROOT_PATH;
if (FM_PATH != '') {
$path .= '/' . FM_PATH;
}
try {
$zip_filename = 'download_' . date('Y-m-d_H-i-s') . '.tar';
$tarfile = FM_ROOT_PATH .'/tmp/'. $zip_filename;
$pd = new \PharData($tarfile);
$pd->buildFromDirectory( $path);
echo 'Compressing all files done, check your server for the file ' .$tarfile ;
} catch (Exception $e) {
echo $e->getMessage();
}
$this->list();
}
Note: You will need a folder called tmp with write permission on the $_SERVER['DOCUMENT_ROOT']
.
I hope someone has time, and energy to make this a real, good usable plugin with more options.
Offline
Pages: 1