mirror of
https://github.com/getgrav/grav.git
synced 2026-07-13 16:42:47 +02:00
Merge branches 'develop' and 'feature/objects' of https://github.com/getgrav/grav into feature/objects
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,3 +1,15 @@
|
||||
# v1.1.18
|
||||
## 02/xx/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added default setting to only allow `direct-installs` from official GPM. Can be configured in `system.yaml`
|
||||
* Added a new `Utils::isValidUrl()` method
|
||||
1. [](#improved)
|
||||
* Genericized `direct-install` so it can be called via Admin plugin
|
||||
1. [](#bugfix)
|
||||
* Fixed a minor bug in Number validation [#1329](https://github.com/getgrav/grav/issues/1329)
|
||||
* Fixed exception when trying to find user account and there is no `user://accounts` folder
|
||||
|
||||
# v1.1.17
|
||||
## 02/17/2017
|
||||
|
||||
|
||||
@@ -1057,6 +1057,19 @@ form:
|
||||
fopen: PLUGIN_ADMIN.FOPEN
|
||||
curl: PLUGIN_ADMIN.CURL
|
||||
|
||||
gpm.official_gpm_only:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY
|
||||
highlight: auto
|
||||
help: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
default: true
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
gpm.verify_peer:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_VERIFY_PEER
|
||||
|
||||
@@ -138,3 +138,4 @@ gpm:
|
||||
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
|
||||
method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
|
||||
verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help.
|
||||
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
|
||||
|
||||
@@ -339,7 +339,7 @@ class Validation
|
||||
|
||||
protected static function filterNumber($value, array $params, array $field)
|
||||
{
|
||||
return (int) $value;
|
||||
return self::validateFloat($value, $params) ? (float) $value : (int) $value;
|
||||
}
|
||||
|
||||
protected static function filterDateTime($value, array $params, array $field)
|
||||
@@ -585,7 +585,7 @@ class Validation
|
||||
$values[$key] = array_map('trim', explode(',', $value));
|
||||
} else {
|
||||
$values[$key] = trim($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Inflector;
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\Utils;
|
||||
@@ -499,6 +500,151 @@ class GPM extends Iterator
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the zip package via the URL
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
public static function downloadPackage($package_file, $tmp)
|
||||
{
|
||||
$package = parse_url($package_file);
|
||||
$filename = basename($package['path']);
|
||||
|
||||
if (Grav::instance()['config']->get('system.gpm.official_gpm_only') && $package['host'] !== 'getgrav.org') {
|
||||
throw new \RuntimeException("Only offical GPM URLs are allowed. You can modify this behavior in the System configuration.");
|
||||
}
|
||||
|
||||
$output = Response::get($package_file, []);
|
||||
|
||||
if ($output) {
|
||||
Folder::mkdir($tmp);
|
||||
file_put_contents($tmp . DS . $filename, $output);
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the local zip package to tmp
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
public static function copyPackage($package_file, $tmp)
|
||||
{
|
||||
$package_file = realpath($package_file);
|
||||
|
||||
if (file_exists($package_file)) {
|
||||
$filename = basename($package_file);
|
||||
Folder::mkdir($tmp);
|
||||
copy(realpath($package_file), $tmp . DS . $filename);
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package type from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function getPackageType($source)
|
||||
{
|
||||
$plugin_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Plugin/m';
|
||||
$theme_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Theme/m';
|
||||
|
||||
if (
|
||||
file_exists($source . 'system/defines.php') &&
|
||||
file_exists($source . 'system/config/system.yaml')
|
||||
) {
|
||||
return 'grav';
|
||||
} else {
|
||||
// must have a blueprint
|
||||
if (!file_exists($source . 'blueprints.yaml')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// either theme or plugin
|
||||
$name = basename($source);
|
||||
if (Utils::contains($name, 'theme')) {
|
||||
return 'theme';
|
||||
} elseif (Utils::contains($name, 'plugin')) {
|
||||
return 'plugin';
|
||||
}
|
||||
foreach (glob($source . "*.php") as $filename) {
|
||||
$contents = file_get_contents($filename);
|
||||
if (preg_match($theme_regex, $contents)) {
|
||||
return 'theme';
|
||||
} elseif (preg_match($plugin_regex, $contents)) {
|
||||
return 'plugin';
|
||||
}
|
||||
}
|
||||
|
||||
// Assume it's a theme
|
||||
return 'theme';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package name from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function getPackageName($source)
|
||||
{
|
||||
foreach (glob($source . "*.yaml") as $filename) {
|
||||
$name = strtolower(basename($filename, '.yaml'));
|
||||
if ($name == 'blueprints') {
|
||||
continue;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find/Parse the blueprint file
|
||||
*
|
||||
* @param $source
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function getBlueprints($source)
|
||||
{
|
||||
$blueprint_file = $source . 'blueprints.yaml';
|
||||
if (!file_exists($blueprint_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$blueprint = (array)Yaml::parse(file_get_contents($blueprint_file));
|
||||
return $blueprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the install path for a name and a particular type of package
|
||||
*
|
||||
* @param $type
|
||||
* @param $name
|
||||
* @return string
|
||||
*/
|
||||
public static function getInstallPath($type, $name)
|
||||
{
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if ($type == 'theme') {
|
||||
$install_path = $locator->findResource('themes://', false) . DS . $name;
|
||||
} else {
|
||||
$install_path = $locator->findResource('plugins://', false) . DS . $name;
|
||||
}
|
||||
return $install_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a list of Packages in the repository
|
||||
* @param array $searches An array of either slugs or names
|
||||
|
||||
@@ -189,6 +189,17 @@ class Response
|
||||
return preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a remote file or not
|
||||
*
|
||||
* @param $file
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRemote($file)
|
||||
{
|
||||
return (bool) filter_var($file, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress normalized for cURL and Fopen
|
||||
* Accepts a variable length of arguments passed in by stream method
|
||||
|
||||
@@ -727,7 +727,7 @@ class Uri
|
||||
*
|
||||
* @return boolean is eternal state
|
||||
*/
|
||||
public function isExternal($url)
|
||||
public static function isExternal($url)
|
||||
{
|
||||
if (Utils::startsWith($url, 'http')) {
|
||||
return true;
|
||||
@@ -1086,4 +1086,20 @@ class Uri
|
||||
|
||||
return $urlWithNonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the passed in URL a valid URL?
|
||||
*
|
||||
* @param $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidUrl($url)
|
||||
{
|
||||
$regex = '/^(?:(https?|ftp|telnet):)?\/\/((?:[a-z0-9@:.-]|%[0-9A-F]{2}){3,})(?::(\d+))?((?:\/(?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\@]|%[0-9A-F]{2})*)*)(?:\?((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?/';
|
||||
if (preg_match($regex, $url)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,14 +57,14 @@ class User extends Data
|
||||
/**
|
||||
* Find a user by username, email, etc
|
||||
*
|
||||
* @param $query the query to search for
|
||||
* @param string $query the query to search for
|
||||
* @param array $fields the fields to search
|
||||
* @return User
|
||||
*/
|
||||
public static function find($query, $fields = ['username', 'email'])
|
||||
{
|
||||
$account_dir = Grav::instance()['locator']->findResource('account://');
|
||||
$files = array_diff(scandir($account_dir), ['.', '..']);
|
||||
$files = $account_dir ? array_diff(scandir($account_dir), ['.', '..']) : [];
|
||||
|
||||
// Try with username first, you never know!
|
||||
if (in_array('username', $fields)) {
|
||||
@@ -100,7 +100,7 @@ class User extends Data
|
||||
public static function remove($username)
|
||||
{
|
||||
$file_path = Grav::instance()['locator']->findResource('account://' . $username . YAML_EXT);
|
||||
if (file_exists($file_path) && unlink($file_path)) {
|
||||
if ($file_path && unlink($file_path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
namespace Grav\Console\Gpm;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class DirectInstallCommand extends ConsoleCommand
|
||||
{
|
||||
@@ -63,10 +62,30 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->writeln("Preparing to install <cyan>" . $package_file . "</cyan>");
|
||||
|
||||
|
||||
if ($this->isRemote($package_file)) {
|
||||
$zip = $this->downloadPackage($package_file, $tmp_zip);
|
||||
if (Response::isRemote($package_file)) {
|
||||
$this->output->write(" |- Downloading package... 0%");
|
||||
try {
|
||||
$zip = GPM::downloadPackage($package_file, $tmp_zip);
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln(" `- <red>ERROR: " . $e->getMessage() . "</red>");
|
||||
$this->output->writeln('');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($zip) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading package... 100%");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
} else {
|
||||
$zip = $this->copyPackage($package_file, $tmp_zip);
|
||||
$this->output->write(" |- Copying package... 0%");
|
||||
$zip = GPM::copyPackage($package_file, $tmp_zip);
|
||||
if ($zip) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Copying package... 100%");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($zip)) {
|
||||
@@ -85,7 +104,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->writeln(" |- Extracting package... <green>ok</green>");
|
||||
|
||||
|
||||
$type = $this->getPackageType($extracted);
|
||||
$type = GPM::getPackageType($extracted);
|
||||
|
||||
if (!$type) {
|
||||
$this->output->writeln(" '- <red>ERROR: Not a valid Grav package</red>");
|
||||
@@ -93,7 +112,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
exit;
|
||||
}
|
||||
|
||||
$blueprint = $this->getBlueprints($extracted);
|
||||
$blueprint = GPM::getBlueprints($extracted);
|
||||
if ($blueprint) {
|
||||
if (isset($blueprint['dependencies'])) {
|
||||
$depencencies = [];
|
||||
@@ -137,7 +156,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->write(" |- Installing package... ");
|
||||
Installer::install($zip, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true], $extracted);
|
||||
} else {
|
||||
$name = $this->getPackageName($extracted);
|
||||
$name = GPM::getPackageName($extracted);
|
||||
|
||||
if (!$name) {
|
||||
$this->output->writeln("<red>ERROR: Name could not be determined.</red> Please specify with --name|-n");
|
||||
@@ -145,7 +164,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
exit;
|
||||
}
|
||||
|
||||
$install_path = $this->getInstallPath($type, $name);
|
||||
$install_path = GPM::getInstallPath($type, $name);
|
||||
$is_update = file_exists($install_path);
|
||||
|
||||
$this->output->write(" |- Checking destination... ");
|
||||
@@ -173,7 +192,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->write("\x0D");
|
||||
|
||||
if(Installer::lastErrorCode()) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln(" '- <red>" . Installer::lastErrorMsg() . "</red>");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
$this->output->writeln(" |- Installing package... <green>ok</green>");
|
||||
@@ -193,186 +212,4 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the install path for a name and a particular type of package
|
||||
*
|
||||
* @param $type
|
||||
* @param $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getInstallPath($type, $name)
|
||||
{
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if ($type == 'theme') {
|
||||
$install_path = $locator->findResource('themes://', false) . DS . $name;
|
||||
} else {
|
||||
$install_path = $locator->findResource('plugins://', false) . DS . $name;
|
||||
}
|
||||
return $install_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package name from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function getPackageName($source)
|
||||
{
|
||||
foreach (glob($source . "*.yaml") as $filename) {
|
||||
$name = strtolower(basename($filename, '.yaml'));
|
||||
if ($name == 'blueprints') {
|
||||
continue;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package type from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function getPackageType($source)
|
||||
{
|
||||
$plugin_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Plugin/m';
|
||||
$theme_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Theme/m';
|
||||
|
||||
if (
|
||||
file_exists($source . 'system/defines.php') &&
|
||||
file_exists($source . 'system/config/system.yaml')
|
||||
) {
|
||||
return 'grav';
|
||||
} else {
|
||||
// must have a blueprint
|
||||
if (!file_exists($source . 'blueprints.yaml')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// either theme or plugin
|
||||
$name = basename($source);
|
||||
if (Utils::contains($name, 'theme')) {
|
||||
return 'theme';
|
||||
} elseif (Utils::contains($name, 'plugin')) {
|
||||
return 'plugin';
|
||||
}
|
||||
foreach (glob($source . "*.php") as $filename) {
|
||||
$contents = file_get_contents($filename);
|
||||
if (preg_match($theme_regex, $contents)) {
|
||||
return 'theme';
|
||||
} elseif (preg_match($plugin_regex, $contents)) {
|
||||
return 'plugin';
|
||||
}
|
||||
}
|
||||
|
||||
// Assume it's a theme
|
||||
return 'theme';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this is a local or a remote file
|
||||
*
|
||||
* @param $file
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRemote($file)
|
||||
{
|
||||
return (bool) filter_var($file, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find/Parse the blueprint file
|
||||
*
|
||||
* @param $source
|
||||
* @return array|bool
|
||||
*/
|
||||
protected function getBlueprints($source)
|
||||
{
|
||||
$blueprint_file = $source . 'blueprints.yaml';
|
||||
if (!file_exists($blueprint_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$blueprint = (array)Yaml::parse(file_get_contents($blueprint_file));
|
||||
return $blueprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the zip package via the URL
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
private function downloadPackage($package_file, $tmp)
|
||||
{
|
||||
$this->output->write(" |- Downloading package... 0%");
|
||||
|
||||
$package = parse_url($package_file);
|
||||
|
||||
|
||||
$filename = basename($package['path']);
|
||||
$output = Response::get($package_file, [], [$this, 'progress']);
|
||||
|
||||
if ($output) {
|
||||
Folder::mkdir($tmp);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading package... 100%");
|
||||
$this->output->writeln('');
|
||||
|
||||
file_put_contents($tmp . DS . $filename, $output);
|
||||
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the local zip package to tmp
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
private function copyPackage($package_file, $tmp)
|
||||
{
|
||||
$this->output->write(" |- Copying package... 0%");
|
||||
|
||||
$package_file = realpath($package_file);
|
||||
|
||||
if (file_exists($package_file)) {
|
||||
$filename = basename($package_file);
|
||||
|
||||
Folder::mkdir($tmp);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Copying package... 100%");
|
||||
$this->output->writeln('');
|
||||
|
||||
copy(realpath($package_file), $tmp . DS . $filename);
|
||||
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $progress
|
||||
*/
|
||||
public function progress($progress)
|
||||
{
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading package... " . str_pad($progress['percent'], 5, " ",
|
||||
STR_PAD_LEFT) . '%');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,6 +675,8 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
{
|
||||
$this->uri->initializeWithURL('http://testing.dev/item2/item2-2')->init();
|
||||
|
||||
$this->assertSame('<p><a href="#something" class="button">Anchor Class</a></p>',
|
||||
$this->parsedown->text('[Anchor Class](?classes=button#something)'));
|
||||
$this->assertSame('<p><a href="/item2/item2-3" class="button">Relative Class</a></p>',
|
||||
$this->parsedown->text('[Relative Class](../item2-3?classes=button)'));
|
||||
$this->assertSame('<p><a href="/item2/item2-3" id="unique">Relative ID</a></p>',
|
||||
|
||||
Reference in New Issue
Block a user