How can I add a method to create files when in theme-editor.php
-
22-04-2021 - |
Domanda
This is a concept of my goal
I want to
be able to create files in the current opened directory by entering the complete file name and and extension in a text field, click create and use the AJAX process to run the file_put_contents()
method.
I haven't tried any coding methods as I do not even have a starting point. After 18 hours of research, I have only found methods using add_theme_page()
to add a page where all the custom coding can be done. While that's a solid option, I'd like to keep it all in the core file editor. It's just an overkill to create an entire file editor which does all the same tasks as the core, with the only diff being a text field.
Update
In an act of desperation, I have used the admin_notices
action to insert the input field and button, and attached a function to show only on the specified theme, and run the file creation process. It's not the solution I desire so I don't think it's appropriate to post as an answer.
public static function makeFile($file, $data=null)
{
if( !is_null(mb::getPost('makecss')) ) {
file_put_contents(DEF_THEMEPATH.'/styles/'.$file.'.css', $data);
wp_redirect( add_query_arg(['file'=>'styles/'.$file.'.css','theme'=>'thor'],admin_url('theme-editor.php')) );
exit;
}
$newfile = '
<div class="newfile-form">
<form method="post" action="">
<p>Create New CSS File</p>
<span>File name: <input type="text" name="newfile" id="newfile" value="" /></span>
<span><button type="submit" name="makecss" class="button button-primary">Create File</button></span>
</form>
</div>
';
return $newfile;
}
action
if( strstr(mb::urlVar('theme'), 'thor') ) {
add_action('admin_notices', function() {
echo mb::makeFile(mb::getPost('newfile'));
});
}
Result
Note: mb represents the name of my php class
Note: Though the .css
extension is shown in the image, I have since made it the default and only extension possible. A javascript method replaces any period and following string with null
Soluzione
I couldn't define an "accurate" method to implement the form I desired, so I have settled on the admin_notices
action as a viable solution.
The Result Illustrations
View when in Theme Editor of the associated theme. The default style.css
is selected, but is not editable. An error message is shown if Update File button is clicked.
Returned view after creating CSS file. When the file is successfully created, the page reloads and open the new file in the code editor.
The CSS file is available for selection in the theme's supporting plugin. If a stylesheet is selected, it will not be listed in the delete list within the Theme Editor.
The Coding
Form process function
public static function makeFile($file, $data=null)
{
if( !is_null(mb::getPost('makecss')) )
{
// suppress default error notice since it is not related
echo '<style>div#message.notice.notice-error {display: none;}</style>';
$file = esc_html($file);
file_put_contents(MBTHEMEDIR.'/styles/'.$file.'.css', $data);
mb::redirect(admin_url('theme-editor.php?file=styles/'.$file.'.css&theme=thor'));
}
//delete file
if( !is_null(mb::getPost('deletecss')) )
{
// suppress default error notice since it is not related
echo '<style>div#message.notice.notice-error {display: none;}</style>';
if( mb::getPost('csslist') != '' ) {
unlink(MBTHEMEDIR.'/styles/'.mb::getPost('csslist'));
mb::redirect(admin_url('theme-editor.php?theme=thor'));
}else{
echo '<div class="notice notice-warning is-dismissible"><p>No file was selected</p></div>';
}
}
$newfile = '
<div class="newfile-form">
<form action="" method="POST">
<p>Create New CSS File</p>
<span>File name: <input type="text" name="newfile" id="newfile" value="" placeholder="mystyle" /></span>
<span><button type="submit" name="makecss" class="button button-primary">Create File</button></span>
<span>
<select name="csslist">
<option value="">None</option>';
foreach(mb::filelist(MBTHEMEDIR.'/styles', 'css') as $css) {
$newfile .= '<option value="'.$css.'">'.$css.'</option>';
}
$newfile .= '</select>
</span>
<span><button type="submit" name="deletecss" class="button">Delete File</button></span>
</form>
</div>
';
return $newfile;
}
Action Hook
if( current_user_can('edit_files') )
{
// confirm that the specified theme is selected
if( strstr(mb::urlVar('theme'), 'thor') )
{
// suppress default WP missing file notice when file create request is sent
echo '<style>div#message.notice.notice-info {display: none;}</style>';
// implement form process action
add_action('admin_notices', function() {
echo mb::makeFile(sanitize_text_field(mb::getPost('newfile')));
});
// action to disable editing core files
add_action('load-theme-editor.php', function()
{
$file = filter_input(INPUT_GET, 'file', FILTER_SANITIZE_STRING);
if( in_array($file, ['style.css', '404.php','index.php']) ) {
wp_redirect(add_query_arg([],self_admin_url('theme-editor.php?theme=thor')));
exit;
}
});
}
}
I had to use a different page redirection method due to header sent errors when using wp_redirect()
public static function redirect($url) {
if( !headers_sent() ) {
wp_redirect(admin_url($url));
}else{
echo '<meta http-equiv="refresh" content="0; URL='.$url.'">';
}
}
The filelist()
method used to output the select options of the delete field. WP has a method for this but I couldn't get it to behave as desired, so I corrected it
public static function filelist($path, $filter=null, $getpath=false)
{
$files = new \DirectoryIterator($path);
$filelist=[];
foreach($files as $file)
{
if( $file->isFile() && !$file->isDot() )
{
// include only files in $filter
// methods: 'css' or 'css|txt' or starting with '^cat' or ending with '$er'
if( !empty($filter) && !preg_match(chr(1).$filter.chr(1), $file) ) {
continue;
}
$filelist[] = ($getpath == true ? $file->getPath().'/'.$file->getFilename() : $file->getFilename());
}
}
return $filelist;
}
The getPost()
method is just a set of global functions to check the form request values. The WP method to do the same task would be applicable.
The urlVar()
method is a set of global functions to check the URL string for query variables and their values. The WP method to do the same would be applicable.
That's my story and I'm sticking with it!