Skip to content

Commit

Permalink
Validate the directory structure of the update
Browse files Browse the repository at this point in the history
 
As noted in the docs, the update ZIP file needs to have a specific directory structure. All plugin files should be inside a directory named "plugin-slug-here" and not at the root of the ZIP file. The update checker can fix the directory name if necessary, but it can't deal with the files being at the wrong level - at least not yet.

The reason this is necessary is that WordPress decides which directory to copy to /wp-content/plugins based on the contents of the update. If the ZIP contains just a single directory, WP will copy that directory. In any other case, it will copy the entire working directory where it extracted the ZIP file. This directory usually has a name like "zip-file-name.tmp". As a result, the directory name of the update won't match the directory where the old version was installed, which means WordPress will fail to reactivate the plugin.

The update checker can't rename the working directory because that would prevent WP from cleaning up temporary files in that directory (that part of the core is not hook-able and doesn't check for errors).

A potential solution would be to automatically create the requisite subdirectory and copy all update files to it. However, that would be a bit too much "magic", as well as another edge case to worry about and test. It seems better to notify the developer and let them fix their mistake.
  • Loading branch information
YahnisElsts committed Feb 22, 2015
1 parent 323e80f commit acb8476
Showing 1 changed file with 22 additions and 2 deletions.
24 changes: 22 additions & 2 deletions plugin-update-checker.php
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,8 @@ public function injectUpdate($updates){
* GitHub and other repositories provide ZIP downloads, but they often use directory names like
* "project-branch" or "project-tag-hash". We need to change the name to the actual plugin folder.
*
* @param string $source
* @param string $remoteSource
* @param string $source The directory to copy to /wp-content/plugins. Usually a subdirectory of $remoteSource.
* @param string $remoteSource WordPress has extracted the update to this directory.
* @param WP_Upgrader $upgrader
* @return string|WP_Error
*/
Expand Down Expand Up @@ -592,6 +592,26 @@ function fixDirectoryName($source, $remoteSource, $upgrader) {
}
$correctedSource = trailingslashit($remoteSource) . $pluginDirectoryName . '/';
if ( $source !== $correctedSource ) {
//The update archive should contain a single directory that contains the rest of plugin files. Otherwise,
//WordPress will try to copy the entire working directory ($source == $remoteSource). We can't rename
//$remoteSource because that would break WordPress code that cleans up temporary files after update.
$sourceFiles = $wp_filesystem->dirlist($remoteSource);
if ( is_array($sourceFiles) ) {
$sourceFiles = array_keys($sourceFiles);
$firstFilePath = trailingslashit($remoteSource) . $sourceFiles[0];

if ( (count($sourceFiles) > 1) || (!$wp_filesystem->is_dir($firstFilePath)) ) {
return new WP_Error(
'puc-incorrect-directory-structure',
sprintf(
'The directory structure of the update is incorrect. All plugin files should be inside ' .
'a directory named <span class="code">%s</span>, not at the root of the ZIP file.',
htmlentities($this->slug)
)
);
}
}

$upgrader->skin->feedback(sprintf(
'Renaming %s to %s&#8230;',
'<span class="code">' . basename($source) . '</span>',
Expand Down

0 comments on commit acb8476

Please sign in to comment.