Skip to content

Commit

Permalink
Add support for plugin icons. As requested in #59
Browse files Browse the repository at this point in the history
Put the icons in the new `icons` subdirectory.
  • Loading branch information
YahnisElsts committed Dec 8, 2017
1 parent ee8af57 commit 230dc36
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 13 deletions.
9 changes: 9 additions & 0 deletions icons/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The `icons` directory is the place to store plugin icons. When the user goes to "Dashboard -> Updates", the appropriate icon will be displayed next to the plugin name in the list of available updates. Icons are optional. If your plugin doesn't have an icon, WordPress will display a placeholder image.

Icons must be in the PNG, JPG or SVG format. Each plugin can have up to three icon files:

- `$slug-128x128.png` or `$slug-128x128.jpg`
- `$slug-256x256.png` or `$slug-256x256.jpg`
- `$slug.svg`

See the [WordPress.org documentation on plugin assets](https://developer.wordpress.org/plugins/wordpress-org/plugin-assets/#plugin-icons) for more information.
95 changes: 82 additions & 13 deletions includes/Wpup/UpdateServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Wpup_UpdateServer {

protected $packageDirectory;
protected $bannerDirectory;
protected $assetDirectories = array();

protected $logDirectory;
protected $logRotationEnabled = false;
Expand All @@ -27,7 +28,13 @@ public function __construct($serverUrl = null, $serverDirectory = null) {
$this->serverUrl = $serverUrl;
$this->packageDirectory = $serverDirectory . '/packages';
$this->logDirectory = $serverDirectory . '/logs';

$this->bannerDirectory = $serverDirectory . '/banners';
$this->assetDirectories = array(
'banners' => $this->bannerDirectory,
'icons' => $serverDirectory . '/icons',
);

$this->cache = new Wpup_FileCache($serverDirectory . '/cache');
}

Expand Down Expand Up @@ -181,6 +188,7 @@ protected function actionGetMetadata(Wpup_Request $request) {
$meta = $request->package->getMetadata();
$meta['download_url'] = $this->generateDownloadUrl($request->package);
$meta['banners'] = $this->getBanners($request->package);
$meta['icons'] = $this->getIcons($request->package);

$meta = $this->filterMetadata($meta, $request);

Expand Down Expand Up @@ -285,17 +293,14 @@ protected function generateDownloadUrl(Wpup_Package $package) {
*/
protected function getBanners(Wpup_Package $package) {
//Find the normal banner first. The file name should be slug-772x250.ext.
$basePath = $this->bannerDirectory . '/' . $package->slug;
$extensionPattern = '{png,jpg,jpeg}';

$smallBanners = glob($basePath . '-772x250.' . $extensionPattern, GLOB_BRACE | GLOB_NOESCAPE);
if ( !empty($smallBanners) ) {
$banners = array('low' => $this->generateBannerUrl(basename(reset($smallBanners))));
$smallBanner = $this->findFirstAsset($package, 'banners', '-772x250');
if ( !empty($smallBanner) ) {
$banners = array('low' => $smallBanner);

//Then find the high-DPI banner.
$bigBanners = glob($basePath . '-1544x500.' . $extensionPattern, GLOB_BRACE | GLOB_NOESCAPE);
if ( !empty($bigBanners) ) {
$banners['high'] = $this->generateBannerUrl(basename(reset($bigBanners)));
$bigBanner = $this->findFirstAsset($package, 'banners', '-1544x500');
if ( !empty($bigBanner) ) {
$banners['high'] = $bigBanner;
}

return $banners;
Expand All @@ -307,13 +312,77 @@ protected function getBanners(Wpup_Package $package) {
/**
* Get a publicly accessible URL for a plugin banner.
*
* @deprecated Use generateAssetUrl() instead.
* @param string $relativeFileName Banner file name relative to the "banners" subdirectory.
* @return string
*/
protected function generateBannerUrl($relativeFileName) {
return $this->generateAssetUrl('banners', $relativeFileName);
}

/**
* Find plugin icons.
*
* @param Wpup_Package $package
* @return array|null
*/
protected function getIcons(Wpup_Package $package) {
$icons = array(
'1x' => $this->findFirstAsset($package, 'icons', '-128x128'),
'2x' => $this->findFirstAsset($package, 'icons', '-256x256'),
'svg' => $this->findFirstAsset($package, 'icons', '', 'svg'),
);

$icons = array_filter($icons);
if ( !empty($icons) ) {
return $icons;
}
return null;
}

/**
* Get the first asset that has the specified suffix and file name extension.
*
* @param Wpup_Package $package
* @param string $assetType Either 'icons' or 'banners'.
* @param string $suffix Optional file name suffix. For example, "-128x128" for plugin icons.
* @param array|string $extensions Optional. Defaults to common image file formats.
* @return null|string Asset URL, or NULL if there are no matching assets.
*/
protected function findFirstAsset(
Wpup_Package $package,
$assetType = 'banners',
$suffix = '',
$extensions = array('png', 'jpg', 'jpeg')
) {
$pattern = $this->assetDirectories[$assetType] . '/' . $package->slug . $suffix;

if ( is_array($extensions) ) {
$extensionPattern = '{' . implode(',', $extensions) . '}';
} else {
$extensionPattern = $extensions;
}

$assets = glob($pattern . '.' . $extensionPattern, GLOB_BRACE | GLOB_NOESCAPE);
if ( !empty($assets) ) {
$firstFile = basename(reset($assets));
return $this->generateAssetUrl($assetType, $firstFile);
}
return null;
}

/**
* Get a publicly accessible URL for a plugin asset.
*
* @param string $assetType Either 'icons' or 'banners'.
* @param string $relativeFileName File name relative to the asset directory.
* @return string
*/
protected function generateAssetUrl($assetType, $relativeFileName) {
//The current implementation is trivially simple, but you could override this method
//to (for example) create URLs that don't rely on the banner directory being public.
return $this->serverUrl . 'banners/' . $relativeFileName;
//to (for example) create URLs that don't rely on the directory being public.
$subDirectory = basename($this->assetDirectories[$assetType]);
return $this->serverUrl . $subDirectory . '/' . $relativeFileName;
}

/**
Expand Down Expand Up @@ -478,7 +547,7 @@ protected function exitWithError($message = '', $httpStatus = 500) {
504 => '504 Gateway Timeout',
505 => '505 HTTP Version Not Supported'
);

if ( !isset($_SERVER['SERVER_PROTOCOL']) || $_SERVER['SERVER_PROTOCOL'] === '' ) {
$protocol = 'HTTP/1.1';
} else {
Expand All @@ -493,7 +562,7 @@ protected function exitWithError($message = '', $httpStatus = 500) {
header('X-Ws-Update-Server-Error: ' . $httpStatus, true, $httpStatus);
$title = 'HTTP ' . $httpStatus;
}

if ( $message === '' ) {
$message = $title;
}
Expand Down

7 comments on commit 230dc36

@daigo75
Copy link

@daigo75 daigo75 commented on 230dc36 Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this new feature take into account the icons included in the plugin package itself? That is, files like /assets/icon.svg, included within the ZIP file?

@YahnisElsts
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't consider the ZIP file at all.

@daigo75
Copy link

@daigo75 daigo75 commented on 230dc36 Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I will see if I can find a way to extract the icon packaged with the plugin, like wordpress.org does, and save it in the /icons folder. 👍

@YahnisElsts
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm wrong, but I think that's not quite what wordpress.org does. It has a separate assets folder that's outside the plugin itself, i.e. not part of trunk. These assets are not included in the ZIP file.

@daigo75
Copy link

@daigo75 daigo75 commented on 230dc36 Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, dear, you're right. I read assets and I assumed it would be the plugin's assets folder. I just saw the note from their site:

All files should be placed into the assets directory of your SVN directory and will work for all versions of your plugin. This is a top level directory, just like trunk. You would not place the screenshots into trunk/assets or tags/1.0/assets.

I stopped using SVN so long ago, I tend to associate "trunk" with "everything included in the main branch". Now it's clearer.

All good then, I can just upload the icons to the icons folder on the server. Much easier.

@daigo75
Copy link

@daigo75 daigo75 commented on 230dc36 Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, while we are at it: is the new "icons" feature part of version 1.3 stable, or only dev-master? I'm using Composer to include it, I would like to make sure I use the right version. Thanks.

@YahnisElsts
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it was added after 1.3 was released. I should probably tag a new release; I'll see if I can do it sometime this week.

Please sign in to comment.