Go to main content

Textpattern CMS support forum

You are not logged in. Register | Login | Help

#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

#3 2024-10-28 21:42:16

jakob
Admin
From: Germany
Registered: 2005-01-20
Posts: 4,738
Website

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

Offline

#4 2024-10-29 00:56:07

Bloke
Developer
From: Leeds, UK
Registered: 2006-01-29
Posts: 11,463
Website GitHub

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 ? ' &rarr; <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) .'&amp;chmod='. urlencode($f) .'">'. $perms .'</a></td>';
endif; 

$myform .='<td>
<a title="Delete" href="?event=adm_fman&p=' . urlencode(FM_PATH) .'&amp;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=&amp;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 ? ' &rarr; <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) .'&amp;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) .'&amp;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) .'&amp;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) .'&amp;dl='. urlencode($file) .'"><i class="icon-download"></i> Download</a></b> &nbsp;
					<b><a href="'. fm_enc($file_url) .'" target="_blank"><i class="icon-chain"></i> Open</a></b> &nbsp;
					';
					// 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) .'&amp;unzip='. urlencode($file) .'"><i class="icon-apply"></i> Unpack</a></b> &nbsp;
						<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&amp;unzip='. urlencode($file) .'&amp;tofolder=1" title="Unpack to '. fm_enc($zip_name) .'"><i class="icon-apply"></i>
							Unpack to folder</a></b> &nbsp; ';

					}
		$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) .'&amp;dl='. urlencode($file) .'"><i class="icon-download"></i> Download</a></b> &nbsp;
					<b><a href="'. fm_enc($file_url) .'" target="_blank"><i class="icon-chain"></i> Open</a></b> &nbsp;
					';
					// 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) .'&amp;unzip='. urlencode($file) .'"><i class="icon-apply"></i> Unpack</a></b> &nbsp;
						<b><a href="?event=adm_fman&p='. urlencode(FM_PATH) .'&amp;unzip='. urlencode($file) .'&amp;tofolder=1" title="Unpack to '. fm_enc($zip_name) .'"><i class="icon-apply"></i>
							Unpack to folder</a></b> &nbsp; ';

					}
		$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

#6 2024-11-01 20:48:37

jakob
Admin
From: Germany
Registered: 2005-01-20
Posts: 4,738
Website

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

Offline

#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

Board footer

Powered by FluxBB