5

I wrote a plugin (https://github.com/bassjobsen/custom-bootstrap-editor) this plugin writes a stylesheet to wp-content/uploads/cbe.

I will use something like the code below to do this:

$upload_dir = wp_upload_dir();
$this->folder = trailingslashit($upload_dir['basedir']).'cbe/';
if( !is_dir( $this->folder ) ) wp_mkdir_p( $this->folder );
if ( is_writable( $this->folder ) ){
    file_put_contents( $this->folder.$this->filename, $css);
}

My first question about the above, what will be the best place to check if wp_upload_dir() is writable? Will there be a standard check (plus error) for this?

After reading http://ottopress.com/2011/tutorial-using-the-wp_filesystem/ i got some other questions too.

The post mentioned tell i should use the Filesystem API to replace the code above. Will this observation still be true?

request_filesystem_credentials seems to work as expected but to login for every file change seems some kind of overkill. Could my plugin skip the login screen?

Althought the credits seems valid my $wp_filesystem->mkdir( $folder ) and $wp_filesystem->put_contents( $folder.'/'.$filename, $css, FS_CHMOD_FILE) seems always return false. How can i debug the Filesystem API calls?

update thanks to @otto file writing works now:

if (isset($_POST['SaveCBESettings'])) {

        if ( !empty($_POST) && check_admin_referer( 'cbe-nonce') ) 
        {



            $SaveCBESettings = 1;
            $in = true;
            $url = wp_nonce_url('options-general.php?page=filewriting','cbe-nonce');
            if (false === ($creds = request_filesystem_credentials($url, '', false, false, array('SaveCBESettings')) ) ) {
                $in = false;
            }
            if ($in && ! WP_Filesystem($creds) ) {
                // our credentials were no good, ask the user for them again
                request_filesystem_credentials($url, '', true, false,array('SaveCBESettings'));
                $in = false;
            }
            if($in)
            {
            // by this point, the $wp_filesystem global should be working, so let's use it to create a file
            global $wp_filesystem;
            $contentdir = trailingslashit( $wp_filesystem->wp_content_dir() ); 
            $wp_filesystem->mkdir( $contentdir. 'cbe' );
            if ( ! $wp_filesystem->put_contents(  $contentdir . 'cbe/test.txt', 'Test file contents', FS_CHMOD_FILE) ) 
            {
                echo "error saving file!";
            }
                unset($_POST);
            }
        }
}
4

Few things to explain here:

In that tutorial, I only chose the upload_dir as an example of how to do it. A demonstration of how the WP_Filesystem functions work. Normally you would not use the WP_Filesystem to write to the upload directory. That code is not meant to be copy-pasta'd into live production code.

Writing CSS, PHP, HTML, or really any other kind of files except images into the upload directory... and which will then be included in the web page in some fashion, is unsafe.

Doesn't really matter how you do it, the upload directory is expected to contain things that are not considered necessarily XSS safe by its very nature. If you need to write files to be included in the page, like stylesheets, then you should make your own folder under /wp-content, not under /wp-content/uploads. The uploads directory should be used strictly for media files and downloads and other things uploaded through the various wp_upload functions.

When using the $wp_filesystem, there's a handy function call for getting the content directory path: $wp_filesystem->wp_content_dir();. You need to use this function because the "remote" directory path may not be the same as the "local" directory path.

There is not a similar function for getting the uploads_dir, because again, you normally would never do that. Really doesn't make a lot of sense to use the uploads dir for those files.

So, this will give you the "remote" path to the content directory, and you can use it to write files and make directories and such like so:

$contentdir = trailingslashit( $wp_filesystem->wp_content_dir() ); 
$wp_filesystem->mkdir( $contentdir. 'cbe' );
$wp_filesystem->put_contents( $contentdir . 'cbe/filename.whatever', $css, FS_CHMOD_FILE);

And so forth. Of course, you still need to request the credentials and instantiate with the WP_Filesystem($creds) call first to use that global $wp_filesystem, but this works.

  • Thanks. File writing works now. I think user will find enter their credentials every save action annoying still. Do i have alternatives? For example create the file on activation and consider it save till deactivation of the plugin? – Bass Jobsen Nov 29 '13 at 23:32
  • Well, you could read it and compare to see if anything changed. If nothing in the file changed, and you have nothing to save, then you don't need to get credentials. – Otto Nov 30 '13 at 1:09
  • Otto, I was wondering if you could expand on your point about uploads_dir. You suggest that the reason for not serving static assets out of there is related to security. Can you describe the details of an exploit that would result from putting, say, a CSS file in /wp-content/uploads/ that would have been prevented by putting it in a custom /wp-content/my-plugin-dir/? I'm having a hard time thinking of one. – Boone Gorges Feb 27 '14 at 16:35
  • 1
    In a lot of shared hosting situations, files in the "uploads" directory are owned by the webserver user account, not by the actual user's account. This means that other people on your shared server may be able to modify those files. If I can modify your CSS files, then I can insert executable script into them for some browsers. See stackoverflow.com/questions/476276/using-javascript-in-css for more info on that. Even if those sort of things are fixed, access to CSS allows for annoyances like body{display:none} and so forth. – Otto Feb 28 '14 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.