mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-11-03 11:55:52 +01:00
[WORK IN PROGRESS] First draft of a Tools menu with direct install (#990)
* First draft of a Tools menu with direct install * Basic styling * Translate GPM messages * Basic frontend validation * Fix form action path * Added lang strings for offical_gpm_only toggle
This commit is contained in:
committed by
Andy Miller
parent
c473a8db84
commit
c7256134ba
12
admin.php
12
admin.php
@@ -88,6 +88,7 @@ class AdminPlugin extends Plugin
|
|||||||
'onShutdown' => ['onShutdown', 1000],
|
'onShutdown' => ['onShutdown', 1000],
|
||||||
'onFormProcessed' => ['onFormProcessed', 0],
|
'onFormProcessed' => ['onFormProcessed', 0],
|
||||||
'onAdminDashboard' => ['onAdminDashboard', 0],
|
'onAdminDashboard' => ['onAdminDashboard', 0],
|
||||||
|
'onAdminTools' => ['onAdminTools', 0],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,6 +726,17 @@ class AdminPlugin extends Plugin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide the tools for the Tools page, currently only direct install
|
||||||
|
*
|
||||||
|
* @return Event
|
||||||
|
*/
|
||||||
|
public function onAdminTools(Event $event)
|
||||||
|
{
|
||||||
|
$event['tools'] = array_merge($event['tools'], [$this->grav['language']->translate('PLUGIN_ADMIN.DIRECT_INSTALL')]);
|
||||||
|
return $event;
|
||||||
|
}
|
||||||
|
|
||||||
public function onAdminDashboard()
|
public function onAdminDashboard()
|
||||||
{
|
{
|
||||||
$this->grav['twig']->plugins_hooked_dashboard_widgets_top[] = ['template' => 'dashboard-maintenance'];
|
$this->grav['twig']->plugins_hooked_dashboard_widgets_top[] = ['template' => 'dashboard-maintenance'];
|
||||||
|
|||||||
@@ -184,6 +184,18 @@ class Admin
|
|||||||
return $configurations;
|
return $configurations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the tools found
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function tools()
|
||||||
|
{
|
||||||
|
$tools = [];
|
||||||
|
$event = Grav::instance()->fireEvent('onAdminTools', new Event(['tools' => &$tools]));
|
||||||
|
return $tools;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the languages available in the site
|
* Return the languages available in the site
|
||||||
*
|
*
|
||||||
@@ -365,8 +377,10 @@ class Admin
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function translate($args, $languages = null)
|
public static function translate($args, $languages = null)
|
||||||
{
|
{
|
||||||
|
$grav = Grav::instance();
|
||||||
|
|
||||||
if (is_array($args)) {
|
if (is_array($args)) {
|
||||||
$lookup = array_shift($args);
|
$lookup = array_shift($args);
|
||||||
} else {
|
} else {
|
||||||
@@ -375,7 +389,7 @@ class Admin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$languages) {
|
if (!$languages) {
|
||||||
$languages = [$this->grav['user']->authenticated ? $this->grav['user']->language : 'en'];
|
$languages = [$grav['user']->authenticated ? $grav['user']->language : 'en'];
|
||||||
} else {
|
} else {
|
||||||
$languages = (array)$languages;
|
$languages = (array)$languages;
|
||||||
}
|
}
|
||||||
@@ -383,25 +397,25 @@ class Admin
|
|||||||
|
|
||||||
if ($lookup) {
|
if ($lookup) {
|
||||||
if (empty($languages) || reset($languages) == null) {
|
if (empty($languages) || reset($languages) == null) {
|
||||||
if ($this->grav['config']->get('system.languages.translations_fallback', true)) {
|
if ($grav['config']->get('system.languages.translations_fallback', true)) {
|
||||||
$languages = $this->grav['language']->getFallbackLanguages();
|
$languages = $grav['language']->getFallbackLanguages();
|
||||||
} else {
|
} else {
|
||||||
$languages = (array)$this->grav['language']->getDefault();
|
$languages = (array)$grav['language']->getDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ((array)$languages as $lang) {
|
foreach ((array)$languages as $lang) {
|
||||||
$translation = $this->grav['language']->getTranslation($lang, $lookup);
|
$translation = $grav['language']->getTranslation($lang, $lookup);
|
||||||
|
|
||||||
if (!$translation) {
|
if (!$translation) {
|
||||||
$language = $this->grav['language']->getDefault() ?: 'en';
|
$language = $grav['language']->getDefault() ?: 'en';
|
||||||
$translation = $this->grav['language']->getTranslation($language, $lookup);
|
$translation = $grav['language']->getTranslation($language, $lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$translation) {
|
if (!$translation) {
|
||||||
$language = 'en';
|
$language = 'en';
|
||||||
$translation = $this->grav['language']->getTranslation($language, $lookup);
|
$translation = $grav['language']->getTranslation($language, $lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($translation) {
|
if ($translation) {
|
||||||
|
|||||||
@@ -2090,4 +2090,30 @@ class AdminController extends AdminBaseController
|
|||||||
|
|
||||||
return $filename . '.md';
|
return $filename . '.md';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle direct install.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function taskDirectInstall()
|
||||||
|
{
|
||||||
|
$file_path = '';
|
||||||
|
|
||||||
|
if (isset($_FILES['uploaded_file'])) {
|
||||||
|
$file_path = $_FILES['uploaded_file']['tmp_name'];
|
||||||
|
} else {
|
||||||
|
$file_path = $this->data['file_path'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Gpm::directInstall($file_path);
|
||||||
|
|
||||||
|
if ($result === true) {
|
||||||
|
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSTALLATION_SUCCESSFUL'), 'info');
|
||||||
|
} else {
|
||||||
|
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSTALLATION_FAILED') . ': ' . $result, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setRedirect('/tools');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,8 @@ class Gpm
|
|||||||
{
|
{
|
||||||
$options = array_merge(self::$options, $options);
|
$options = array_merge(self::$options, $options);
|
||||||
|
|
||||||
if (
|
if (!Installer::isGravInstance($options['destination']) || !Installer::isValidDestination($options['destination'],
|
||||||
!Installer::isGravInstance($options['destination'])
|
[Installer::EXISTS, Installer::IS_LINK])
|
||||||
|| !Installer::isValidDestination($options['destination'], [Installer::EXISTS, Installer::IS_LINK])
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -198,10 +197,15 @@ class Gpm
|
|||||||
* Direct install a file
|
* Direct install a file
|
||||||
*
|
*
|
||||||
* @param $package_file
|
* @param $package_file
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function directInstall($package_file)
|
public static function directInstall($package_file)
|
||||||
{
|
{
|
||||||
|
if (!$package_file) {
|
||||||
|
return Admin::translate('PLUGIN_ADMIN.NO_PACKAGE_NAME');
|
||||||
|
}
|
||||||
|
|
||||||
$tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true);
|
$tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true);
|
||||||
$tmp_zip = $tmp_dir . '/Grav-' . uniqid();
|
$tmp_zip = $tmp_dir . '/Grav-' . uniqid();
|
||||||
|
|
||||||
@@ -216,49 +220,50 @@ class Gpm
|
|||||||
$extracted = Installer::unZip($zip, $tmp_source);
|
$extracted = Installer::unZip($zip, $tmp_source);
|
||||||
|
|
||||||
if (!$extracted) {
|
if (!$extracted) {
|
||||||
return "Package extraction failed.";
|
return Admin::translate('PLUGIN_ADMIN.PACKAGE_EXTRACTION_FAILED');
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = GravGPM::getPackageType($extracted);
|
$type = GravGPM::getPackageType($extracted);
|
||||||
|
|
||||||
if (!$type) {
|
if (!$type) {
|
||||||
return "Not a valid Grav package";
|
return Admin::translate('PLUGIN_ADMIN.NOT_VALID_GRAV_PACKAGE');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type == 'grav') {
|
if ($type == 'grav') {
|
||||||
Installer::isValidDestination(GRAV_ROOT . '/system');
|
Installer::isValidDestination(GRAV_ROOT . '/system');
|
||||||
if (Installer::IS_LINK === Installer::lastErrorCode()) {
|
if (Installer::IS_LINK === Installer::lastErrorCode()) {
|
||||||
return "Cannot overwrite symlinks";
|
return Admin::translate('PLUGIN_ADMIN.CANNOT_OVERWRITE_SYMLINKS');
|
||||||
}
|
}
|
||||||
Installer::install($zip, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true], $extracted);
|
Installer::install($zip, GRAV_ROOT,
|
||||||
|
['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true], $extracted);
|
||||||
} else {
|
} else {
|
||||||
$name = GravGPM::getPackageName($extracted);
|
$name = GravGPM::getPackageName($extracted);
|
||||||
|
|
||||||
if (!$name) {
|
if (!$name) {
|
||||||
return "Name could not be determined";
|
return Admin::translate('PLUGIN_ADMIN.NAME_COULD_NOT_BE_DETERMINED');
|
||||||
}
|
}
|
||||||
|
|
||||||
$install_path = GravGPM::getInstallPath($type, $name);
|
$install_path = GravGPM::getInstallPath($type, $name);
|
||||||
$is_update = file_exists($install_path);
|
$is_update = file_exists($install_path);
|
||||||
|
|
||||||
|
|
||||||
Installer::isValidDestination(GRAV_ROOT . DS . $install_path);
|
Installer::isValidDestination(GRAV_ROOT . DS . $install_path);
|
||||||
if (Installer::lastErrorCode() == Installer::IS_LINK) {
|
if (Installer::lastErrorCode() == Installer::IS_LINK) {
|
||||||
return "Cannot overwrite symlinks";
|
return Admin::translate('PLUGIN_ADMIN.CANNOT_OVERWRITE_SYMLINKS');
|
||||||
}
|
}
|
||||||
|
|
||||||
Installer::install($zip, GRAV_ROOT, ['install_path' => $install_path, 'theme' => (($type == 'theme')), 'is_update' => $is_update], $extracted);
|
Installer::install($zip, GRAV_ROOT,
|
||||||
|
['install_path' => $install_path, 'theme' => (($type == 'theme')), 'is_update' => $is_update],
|
||||||
|
$extracted);
|
||||||
}
|
}
|
||||||
|
|
||||||
Folder::delete($tmp_source);
|
Folder::delete($tmp_source);
|
||||||
|
|
||||||
|
|
||||||
if (Installer::lastErrorCode()) {
|
if (Installer::lastErrorCode()) {
|
||||||
return Installer::lastErrorMsg();
|
return Installer::lastErrorMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return "ZIP package could not be found";
|
return Admin::translate('PLUGIN_ADMIN.ZIP_PACKAGE_NOT_FOUND');
|
||||||
}
|
}
|
||||||
|
|
||||||
Folder::delete($tmp_zip);
|
Folder::delete($tmp_zip);
|
||||||
@@ -276,14 +281,11 @@ class Gpm
|
|||||||
$query = '';
|
$query = '';
|
||||||
|
|
||||||
if ($package->premium) {
|
if ($package->premium) {
|
||||||
$query = \json_encode(array_merge(
|
$query = \json_encode(array_merge($package->premium, [
|
||||||
$package->premium,
|
|
||||||
[
|
|
||||||
'slug' => $package->slug,
|
'slug' => $package->slug,
|
||||||
'filename' => $package->premium['filename'],
|
'filename' => $package->premium['filename'],
|
||||||
'license_key' => $license
|
'license_key' => $license
|
||||||
]
|
]));
|
||||||
));
|
|
||||||
|
|
||||||
$query = '?d=' . base64_encode($query);
|
$query = '?d=' . base64_encode($query);
|
||||||
}
|
}
|
||||||
@@ -297,9 +299,7 @@ class Gpm
|
|||||||
$tmp_dir = Admin::getTempDir() . '/Grav-' . uniqid();
|
$tmp_dir = Admin::getTempDir() . '/Grav-' . uniqid();
|
||||||
Folder::mkdir($tmp_dir);
|
Folder::mkdir($tmp_dir);
|
||||||
|
|
||||||
$bad_chars = array_merge(
|
$bad_chars = array_merge(array_map('chr', range(0, 31)), ["<", ">", ":", '"', "/", "\\", "|", "?", "*"]);
|
||||||
array_map('chr', range(0, 31)),
|
|
||||||
array("<", ">", ":", '"', "/", "\\", "|", "?", "*"));
|
|
||||||
|
|
||||||
$filename = $package->slug . str_replace($bad_chars, "", basename($package->zipball_url));
|
$filename = $package->slug . str_replace($bad_chars, "", basename($package->zipball_url));
|
||||||
|
|
||||||
@@ -345,8 +345,7 @@ class Gpm
|
|||||||
$error[] = '<p>Grav has increased the minimum PHP requirement.<br />';
|
$error[] = '<p>Grav has increased the minimum PHP requirement.<br />';
|
||||||
$error[] = 'You are currently running PHP <strong>' . PHP_VERSION . '</strong>';
|
$error[] = 'You are currently running PHP <strong>' . PHP_VERSION . '</strong>';
|
||||||
$error[] = ', but PHP <strong>' . GRAV_PHP_MIN . '</strong> is required.</p>';
|
$error[] = ', but PHP <strong>' . GRAV_PHP_MIN . '</strong> is required.</p>';
|
||||||
$error[] =
|
$error[] = '<p><a href="http://getgrav.org/blog/changing-php-requirements-to-5.5" class="button button-small secondary">Additional information</a></p>';
|
||||||
'<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));
|
Installer::setError(implode("\n", $error));
|
||||||
|
|
||||||
@@ -357,8 +356,7 @@ class Gpm
|
|||||||
$tmp = Admin::getTempDir() . '/Grav-' . uniqid();
|
$tmp = Admin::getTempDir() . '/Grav-' . uniqid();
|
||||||
$file = self::_downloadSelfupgrade($update, $tmp);
|
$file = self::_downloadSelfupgrade($update, $tmp);
|
||||||
|
|
||||||
Installer::install($file, GRAV_ROOT,
|
Installer::install($file, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true]);
|
||||||
['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true]);
|
|
||||||
|
|
||||||
$errorCode = Installer::lastErrorCode();
|
$errorCode = Installer::lastErrorCode();
|
||||||
|
|
||||||
|
|||||||
@@ -645,3 +645,13 @@ PLUGIN_ADMIN:
|
|||||||
PACKAGE_X_REINSTALLED_SUCCESSFULLY: "Package %s reinstalled successfully"
|
PACKAGE_X_REINSTALLED_SUCCESSFULLY: "Package %s reinstalled successfully"
|
||||||
REINSTALLATION_FAILED: "Reinstallation failed"
|
REINSTALLATION_FAILED: "Reinstallation failed"
|
||||||
WARNING_REINSTALL_NOT_LATEST_RELEASE: "The installed version is not the latest release. By clicking Continue, you'll remove the current version and install the latest available release"
|
WARNING_REINSTALL_NOT_LATEST_RELEASE: "The installed version is not the latest release. By clicking Continue, you'll remove the current version and install the latest available release"
|
||||||
|
TOOLS: "Tools"
|
||||||
|
DIRECT_INSTALL: "Direct Install"
|
||||||
|
NO_PACKAGE_NAME: "Package name not specified"
|
||||||
|
PACKAGE_EXTRACTION_FAILED: "Package extraction failed"
|
||||||
|
NOT_VALID_GRAV_PACKAGE: "Not a valid Grav package"
|
||||||
|
NAME_COULD_NOT_BE_DETERMINED: "Name could not be determined"
|
||||||
|
CANNOT_OVERWRITE_SYMLINKS: "Cannot overwrite symlinks"
|
||||||
|
ZIP_PACKAGE_NOT_FOUND: "ZIP package could not be found"
|
||||||
|
GPM_OFFICIAL_ONLY: "Official GPM Only"
|
||||||
|
GPM_OFFICIAL_ONLY_HELP: "Only allow direct installs from the official GPM repository only."
|
||||||
7
pages/admin/tools.md
Normal file
7
pages/admin/tools.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: Grav Tools
|
||||||
|
|
||||||
|
access:
|
||||||
|
admin.tools: true
|
||||||
|
admin.super: true
|
||||||
|
---
|
||||||
@@ -1086,3 +1086,14 @@ body.sidebar-quickopen #admin-main {
|
|||||||
.pointer-events-disabled {
|
.pointer-events-disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Direct install
|
||||||
|
|
||||||
|
.direct-install-content {
|
||||||
|
padding: 30px;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
{% if config.plugins.admin.sidebar.activate != 'hover' %}
|
{% if config.plugins.admin.sidebar.activate != 'hover' %}
|
||||||
<div id="open-handle" data-sidebar-toggle><i class="fa fa-angle-double-right"></i></div>
|
<div id="open-handle" data-sidebar-toggle><i class="fa fa-angle-double-right"></i></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div id="admin-logo">
|
<div id="admin-logo">
|
||||||
{% include 'partials/logo.html.twig' %}
|
{% include 'partials/logo.html.twig' %}
|
||||||
</div>
|
</div>
|
||||||
@@ -66,6 +67,14 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if authorize(['admin.tools', 'admin.super']) %}
|
||||||
|
<li class="{{ (location == 'tools') ? 'selected' : '' }}">
|
||||||
|
<a href="{{ base_url_relative }}/tools">
|
||||||
|
<i class="fa fa-fw fa-briefcase"></i>
|
||||||
|
<em>{{ "PLUGIN_ADMIN.TOOLS"|tu }}</em>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% include 'nav-pro.html.twig' ignore missing %}
|
{% include 'nav-pro.html.twig' ignore missing %}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<h1>
|
||||||
|
Direct install of packages
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="direct-install-content">
|
||||||
|
|
||||||
|
<h2>Install a zip package by uploading it</h2>
|
||||||
|
|
||||||
|
<form action="{{ base_url_relative }}/tools/task:directInstall" method="post" enctype="multipart/form-data">
|
||||||
|
<input type="file" name="uploaded_file" id="uploaded_file" required accept="application/zip, application/octet-stream">
|
||||||
|
<input type="submit" value="Upload and install" name="submit" class="button">
|
||||||
|
<input type="hidden" name="task" value="directInstall" />
|
||||||
|
|
||||||
|
{{ nonce_field('admin-form', 'admin-nonce')|raw }}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Install a zip package from a remote location</h2>
|
||||||
|
|
||||||
|
<form action="{{ base_url_relative }}/tools/task:directInstall" method="post">
|
||||||
|
<input type="text" name="file_path" required />
|
||||||
|
<input type="submit" value="Install" name="submit" class="button">
|
||||||
|
<input type="hidden" name="task" value="directInstall" />
|
||||||
|
|
||||||
|
{{ nonce_field('admin-form', 'admin-nonce')|raw }}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
28
themes/grav/templates/tools.html.twig
Normal file
28
themes/grav/templates/tools.html.twig
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{% extends 'partials/base.html.twig' %}
|
||||||
|
|
||||||
|
{% set tools_slug = uri.basename %}
|
||||||
|
{% if tools_slug == 'tools' %}{% set tools_slug = 'direct-install' %}{% endif %}
|
||||||
|
{% set title = "PLUGIN_ADMIN.TOOLS"|tu ~ ": " ~ ("PLUGIN_ADMIN." ~ tools_slug|underscorize|upper)|tu %}
|
||||||
|
|
||||||
|
{% block titlebar %}
|
||||||
|
<h1><i class="fa fa-fw fa-wrench"></i> {{ "PLUGIN_ADMIN.TOOLS"|tu }} - {{ ("PLUGIN_ADMIN." ~ tools_slug|underscorize|upper)|tu }}</h1>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content_top %}
|
||||||
|
<ul class="tab-bar">
|
||||||
|
{% for tool in admin.tools %}
|
||||||
|
<li {% if tools_slug == tool|hyphenize %}class="active"{% endif %}>
|
||||||
|
{% if tools_slug == tool %}<span>{% else %}<a href="{{ base_url_relative }}/tools/{{tool|hyphenize}}">{% endif %}
|
||||||
|
{{ tool|tu|capitalize }}
|
||||||
|
{% if tools_slug == tool %}</span>{% else %}</a>{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% if authorize(['admin.tools', 'admin.super']) %}
|
||||||
|
{% include 'partials/tools-' ~ tools_slug ~ '.html.twig' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
Reference in New Issue
Block a user