Files
Grav-Admin-Plugin/classes/gpm.php

374 lines
11 KiB
PHP
Raw Normal View History

2015-04-13 21:37:12 +02:00
<?php
2017-01-20 13:55:03 -08:00
2015-04-13 21:37:12 +02:00
namespace Grav\Plugin\Admin;
2016-02-04 15:42:43 -07:00
use Grav\Common\Grav;
2015-04-13 21:37:12 +02:00
use Grav\Common\GPM\GPM as GravGPM;
use Grav\Common\GPM\Licenses;
2015-04-13 21:37:12 +02:00
use Grav\Common\GPM\Installer;
use Grav\Common\GPM\Response;
use Grav\Common\GPM\Upgrader;
2015-04-13 21:37:12 +02:00
use Grav\Common\Filesystem\Folder;
use Grav\Common\GPM\Common\Package;
use Grav\Plugin\Admin\Admin;
2015-04-13 21:37:12 +02:00
2016-03-01 19:50:12 +01:00
/**
* Class Gpm
2017-01-20 13:55:03 -08:00
*
2016-03-01 19:50:12 +01:00
* @package Grav\Plugin\Admin
*/
2015-04-13 21:37:12 +02:00
class Gpm
{
// Probably should move this to Grav DI container?
2016-03-01 19:50:12 +01:00
/** @var GravGPM */
2015-04-13 21:37:12 +02:00
protected static $GPM;
2016-03-01 19:50:12 +01:00
2015-04-13 21:37:12 +02:00
public static function GPM()
{
if (!static::$GPM) {
static::$GPM = new GravGPM();
}
2016-07-07 18:55:52 +02:00
2015-04-13 21:37:12 +02:00
return static::$GPM;
}
/**
* Default options for the install
2017-01-20 13:55:03 -08:00
*
2015-04-13 21:37:12 +02:00
* @var array
*/
protected static $options = [
'destination' => GRAV_ROOT,
'overwrite' => true,
'ignore_symlinks' => true,
'skip_invalid' => true,
'install_deps' => true,
'theme' => false
2015-04-13 21:37:12 +02:00
];
2016-01-21 09:46:38 +02:00
/**
* @param Package[]|string[]|string $packages
2016-07-07 18:55:52 +02:00
* @param array $options
*
2016-01-21 09:46:38 +02:00
* @return bool
*/
public static function install($packages, array $options)
2015-04-13 21:37:12 +02:00
{
$options = array_merge(self::$options, $options);
if (
2017-01-20 13:55:03 -08:00
!Installer::isGravInstance($options['destination'])
|| !Installer::isValidDestination($options['destination'], [Installer::EXISTS, Installer::IS_LINK])
2015-04-13 21:37:12 +02:00
) {
return false;
}
2016-07-07 18:55:52 +02:00
$packages = is_array($packages) ? $packages : [$packages];
2017-01-20 13:55:03 -08:00
$count = count($packages);
2015-04-13 21:37:12 +02:00
$packages = array_filter(array_map(function ($p) {
return !is_string($p) ? $p instanceof Package ? $p : false : self::GPM()->findPackage($p);
}, $packages));
if (!$options['skip_invalid'] && $count !== count($packages)) {
return false;
}
$messages = '';
2015-04-13 21:37:12 +02:00
foreach ($packages as $package) {
if (isset($package->dependencies) && $options['install_deps']) {
$result = static::install($package->dependencies, $options);
if (!$result) {
return false;
}
}
// Check destination
Installer::isValidDestination($options['destination'] . DS . $package->install_path);
if (Installer::lastErrorCode() === Installer::EXISTS && !$options['overwrite']) {
return false;
}
if (Installer::lastErrorCode() === Installer::IS_LINK && !$options['ignore_symlinks']) {
return false;
}
$license = Licenses::get($package->slug);
2017-01-20 13:55:03 -08:00
$local = static::download($package, $license);
2015-04-13 21:37:12 +02:00
2016-07-07 18:55:52 +02:00
Installer::install($local, $options['destination'],
['install_path' => $package->install_path, 'theme' => $options['theme']]);
2015-04-13 21:37:12 +02:00
Folder::delete(dirname($local));
$errorCode = Installer::lastErrorCode();
if ($errorCode) {
$msg = Installer::lastErrorMsg();
throw new \RuntimeException($msg);
}
if (count($packages) == 1) {
$message = Installer::getMessage();
if ($message) {
return $message;
} else {
$messages .= $message;
}
2015-04-13 21:37:12 +02:00
}
}
return $messages ?: true;
2015-04-13 21:37:12 +02:00
}
2016-01-21 09:46:38 +02:00
/**
* @param Package[]|string[]|string $packages
2016-07-07 18:55:52 +02:00
* @param array $options
*
2016-01-21 09:46:38 +02:00
* @return bool
*/
public static function update($packages, array $options)
2015-04-13 21:37:12 +02:00
{
$options['overwrite'] = true;
2016-01-21 09:46:38 +02:00
2015-04-13 21:37:12 +02:00
return static::install($packages, $options);
}
2016-01-21 09:46:38 +02:00
/**
* @param Package[]|string[]|string $packages
2016-07-07 18:55:52 +02:00
* @param array $options
*
2016-01-21 09:46:38 +02:00
* @return bool
*/
public static function uninstall($packages, array $options)
2015-04-13 21:37:12 +02:00
{
$options = array_merge(self::$options, $options);
2016-07-07 18:55:52 +02:00
$packages = is_array($packages) ? $packages : [$packages];
2017-01-20 13:55:03 -08:00
$count = count($packages);
2015-04-13 21:37:12 +02:00
$packages = array_filter(array_map(function ($p) {
if (is_string($p)) {
2017-01-20 13:55:03 -08:00
$p = strtolower($p);
2015-04-13 21:37:12 +02:00
$plugin = static::GPM()->getInstalledPlugin($p);
2017-01-20 13:55:03 -08:00
$p = $plugin ?: static::GPM()->getInstalledTheme($p);
2015-04-13 21:37:12 +02:00
}
return $p instanceof Package ? $p : false;
}, $packages));
if (!$options['skip_invalid'] && $count !== count($packages)) {
return false;
}
foreach ($packages as $package) {
2016-02-04 20:59:08 +01:00
$location = Grav::instance()['locator']->findResource($package->package_type . '://' . $package->slug);
2015-04-13 21:37:12 +02:00
// Check destination
Installer::isValidDestination($location);
if (Installer::lastErrorCode() === Installer::IS_LINK && !$options['ignore_symlinks']) {
return false;
}
Installer::uninstall($location);
$errorCode = Installer::lastErrorCode();
if ($errorCode && $errorCode !== Installer::IS_LINK && $errorCode !== Installer::EXISTS) {
$msg = Installer::lastErrorMsg();
throw new \RuntimeException($msg);
}
if (count($packages) == 1) {
$message = Installer::getMessage();
if ($message) {
return $message;
}
2015-04-13 21:37:12 +02:00
}
}
return true;
}
2017-02-21 08:23:09 -07:00
/**
* Direct install a file
*
* @param $package_file
* @return bool
*/
public static function directInstall($package_file)
{
$tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true);
$tmp_zip = $tmp_dir . '/Grav-' . uniqid();
if (Response::isRemote($package_file)) {
$zip = GravGPM::downloadPackage($package_file, $tmp_zip);
} else {
$zip = GravGPM::copyPackage($package_file, $tmp_zip);
}
if (file_exists($zip)) {
$tmp_source = $tmp_dir . '/Grav-' . uniqid();
$extracted = Installer::unZip($zip, $tmp_source);
if (!$extracted) {
return "Package extraction failed.";
}
$type = GravGPM::getPackageType($extracted);
if (!$type) {
return "Not a valid Grav package";
}
if ($type == 'grav') {
Installer::isValidDestination(GRAV_ROOT . '/system');
if (Installer::IS_LINK === Installer::lastErrorCode()) {
return "Cannot overwrite symlinks";
}
Installer::install($zip, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true], $extracted);
} else {
$name = GravGPM::getPackageName($extracted);
if (!$name) {
return "Name could not be determined";
}
$install_path = GravGPM::getInstallPath($type, $name);
$is_update = file_exists($install_path);
Installer::isValidDestination(GRAV_ROOT . DS . $install_path);
if (Installer::lastErrorCode() == Installer::IS_LINK) {
return "Cannot overwrite symlinks";
}
Installer::install($zip, GRAV_ROOT, ['install_path' => $install_path, 'theme' => (($type == 'theme')), 'is_update' => $is_update], $extracted);
}
Folder::delete($tmp_source);
if(Installer::lastErrorCode()) {
return Installer::lastErrorMsg();
}
} else {
return "ZIP package could not be found";
}
Folder::delete($tmp_zip);
return true;
}
2016-01-21 09:46:38 +02:00
/**
* @param Package $package
2016-07-07 18:55:52 +02:00
*
2016-01-21 09:46:38 +02:00
* @return string
*/
private static function download(Package $package, $license = null)
2015-04-13 21:37:12 +02:00
{
$query = '';
if ($package->premium) {
$query = \json_encode(array_merge(
$package->premium,
[
2017-01-20 13:55:03 -08:00
'slug' => $package->slug,
'filename' => $package->premium['filename'],
'license_key' => $license
]
));
$query = '?d=' . base64_encode($query);
}
try {
$contents = Response::get($package->zipball_url . $query, []);
} catch (\Exception $e) {
throw new \RuntimeException($e->getMessage());
}
2015-04-13 21:37:12 +02:00
$tmp_dir = Admin::getTempDir() . '/Grav-' . uniqid();
Folder::mkdir($tmp_dir);
2015-04-13 21:37:12 +02:00
$bad_chars = array_merge(
array_map('chr', range(0, 31)),
array("<", ">", ":", '"', "/", "\\", "|", "?", "*"));
$filename = $package->slug . str_replace($bad_chars, "", basename($package->zipball_url));
2015-04-13 21:37:12 +02:00
file_put_contents($tmp_dir . DS . $filename . '.zip', $contents);
2015-04-13 21:37:12 +02:00
return $tmp_dir . DS . $filename . '.zip';
2015-04-13 21:37:12 +02:00
}
2016-01-21 09:46:38 +02:00
/**
2016-07-07 18:55:52 +02:00
* @param array $package
2016-01-21 09:46:38 +02:00
* @param string $tmp
2016-07-07 18:55:52 +02:00
*
2016-01-21 09:46:38 +02:00
* @return string
*/
private static function _downloadSelfupgrade(array $package, $tmp)
{
$output = Response::get($package['download'], []);
Folder::mkdir($tmp);
file_put_contents($tmp . DS . $package['name'], $output);
2016-07-07 18:55:52 +02:00
return $tmp . DS . $package['name'];
}
2016-01-21 09:46:38 +02:00
/**
* @return bool
*/
2016-01-10 17:17:04 +01:00
public static function selfupgrade()
{
$upgrader = new Upgrader();
if (!Installer::isGravInstance(GRAV_ROOT)) {
return false;
}
if (is_link(GRAV_ROOT . DS . 'index.php')) {
Installer::setError(Installer::IS_LINK);
2016-07-07 18:55:52 +02:00
return false;
}
if (method_exists($upgrader, 'meetsRequirements') && !$upgrader->meetsRequirements()) {
2017-01-20 13:55:03 -08:00
$error = [];
$error[] = '<p>Grav has increased the minimum PHP requirement.<br />';
2016-07-07 18:55:52 +02:00
$error[] = 'You are currently running PHP <strong>' . PHP_VERSION . '</strong>';
$error[] = ', but PHP <strong>' . GRAV_PHP_MIN . '</strong> is required.</p>';
2017-01-20 13:55:03 -08:00
$error[] =
'<p><a href="http://getgrav.org/blog/changing-php-requirements-to-5.5" class="button button-small secondary">Additional information</a></p>';
Installer::setError(implode("\n", $error));
2016-07-07 18:55:52 +02:00
return false;
}
$update = $upgrader->getAssets()['grav-update'];
2017-01-20 13:55:03 -08:00
$tmp = Admin::getTempDir() . '/Grav-' . uniqid();
$file = self::_downloadSelfupgrade($update, $tmp);
Installer::install($file, GRAV_ROOT,
['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true]);
$errorCode = Installer::lastErrorCode();
Folder::delete($tmp);
if ($errorCode & (Installer::ZIP_OPEN_ERROR | Installer::ZIP_EXTRACT_ERROR)) {
return false;
}
return true;
}
}