mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-11-02 03:16:11 +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],
|
||||
'onFormProcessed' => ['onFormProcessed', 0],
|
||||
'onAdminDashboard' => ['onAdminDashboard', 0],
|
||||
'onAdminTools' => ['onAdminTools', 0],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -725,6 +726,17 @@ class AdminPlugin extends Plugin
|
||||
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()
|
||||
{
|
||||
$this->grav['twig']->plugins_hooked_dashboard_widgets_top[] = ['template' => 'dashboard-maintenance'];
|
||||
|
||||
@@ -184,6 +184,18 @@ class Admin
|
||||
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
|
||||
*
|
||||
@@ -365,8 +377,10 @@ class Admin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function translate($args, $languages = null)
|
||||
public static function translate($args, $languages = null)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
if (is_array($args)) {
|
||||
$lookup = array_shift($args);
|
||||
} else {
|
||||
@@ -375,7 +389,7 @@ class Admin
|
||||
}
|
||||
|
||||
if (!$languages) {
|
||||
$languages = [$this->grav['user']->authenticated ? $this->grav['user']->language : 'en'];
|
||||
$languages = [$grav['user']->authenticated ? $grav['user']->language : 'en'];
|
||||
} else {
|
||||
$languages = (array)$languages;
|
||||
}
|
||||
@@ -383,25 +397,25 @@ class Admin
|
||||
|
||||
if ($lookup) {
|
||||
if (empty($languages) || reset($languages) == null) {
|
||||
if ($this->grav['config']->get('system.languages.translations_fallback', true)) {
|
||||
$languages = $this->grav['language']->getFallbackLanguages();
|
||||
if ($grav['config']->get('system.languages.translations_fallback', true)) {
|
||||
$languages = $grav['language']->getFallbackLanguages();
|
||||
} else {
|
||||
$languages = (array)$this->grav['language']->getDefault();
|
||||
$languages = (array)$grav['language']->getDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ((array)$languages as $lang) {
|
||||
$translation = $this->grav['language']->getTranslation($lang, $lookup);
|
||||
$translation = $grav['language']->getTranslation($lang, $lookup);
|
||||
|
||||
if (!$translation) {
|
||||
$language = $this->grav['language']->getDefault() ?: 'en';
|
||||
$translation = $this->grav['language']->getTranslation($language, $lookup);
|
||||
$language = $grav['language']->getDefault() ?: 'en';
|
||||
$translation = $grav['language']->getTranslation($language, $lookup);
|
||||
}
|
||||
|
||||
if (!$translation) {
|
||||
$language = 'en';
|
||||
$translation = $this->grav['language']->getTranslation($language, $lookup);
|
||||
$translation = $grav['language']->getTranslation($language, $lookup);
|
||||
}
|
||||
|
||||
if ($translation) {
|
||||
|
||||
@@ -2090,4 +2090,30 @@ class AdminController extends AdminBaseController
|
||||
|
||||
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,15 +56,14 @@ class Gpm
|
||||
{
|
||||
$options = array_merge(self::$options, $options);
|
||||
|
||||
if (
|
||||
!Installer::isGravInstance($options['destination'])
|
||||
|| !Installer::isValidDestination($options['destination'], [Installer::EXISTS, Installer::IS_LINK])
|
||||
if (!Installer::isGravInstance($options['destination']) || !Installer::isValidDestination($options['destination'],
|
||||
[Installer::EXISTS, Installer::IS_LINK])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$packages = is_array($packages) ? $packages : [$packages];
|
||||
$count = count($packages);
|
||||
$count = count($packages);
|
||||
|
||||
$packages = array_filter(array_map(function ($p) {
|
||||
return !is_string($p) ? $p instanceof Package ? $p : false : self::GPM()->findPackage($p);
|
||||
@@ -97,7 +96,7 @@ class Gpm
|
||||
}
|
||||
|
||||
$license = Licenses::get($package->slug);
|
||||
$local = static::download($package, $license);
|
||||
$local = static::download($package, $license);
|
||||
|
||||
Installer::install($local, $options['destination'],
|
||||
['install_path' => $package->install_path, 'theme' => $options['theme']]);
|
||||
@@ -146,14 +145,14 @@ class Gpm
|
||||
$options = array_merge(self::$options, $options);
|
||||
|
||||
$packages = is_array($packages) ? $packages : [$packages];
|
||||
$count = count($packages);
|
||||
$count = count($packages);
|
||||
|
||||
$packages = array_filter(array_map(function ($p) {
|
||||
|
||||
if (is_string($p)) {
|
||||
$p = strtolower($p);
|
||||
$p = strtolower($p);
|
||||
$plugin = static::GPM()->getInstalledPlugin($p);
|
||||
$p = $plugin ?: static::GPM()->getInstalledTheme($p);
|
||||
$p = $plugin ?: static::GPM()->getInstalledTheme($p);
|
||||
}
|
||||
|
||||
return $p instanceof Package ? $p : false;
|
||||
@@ -198,10 +197,15 @@ class Gpm
|
||||
* Direct install a file
|
||||
*
|
||||
* @param $package_file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
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_zip = $tmp_dir . '/Grav-' . uniqid();
|
||||
|
||||
@@ -216,49 +220,50 @@ class Gpm
|
||||
$extracted = Installer::unZip($zip, $tmp_source);
|
||||
|
||||
if (!$extracted) {
|
||||
return "Package extraction failed.";
|
||||
return Admin::translate('PLUGIN_ADMIN.PACKAGE_EXTRACTION_FAILED');
|
||||
}
|
||||
|
||||
$type = GravGPM::getPackageType($extracted);
|
||||
|
||||
if (!$type) {
|
||||
return "Not a valid Grav package";
|
||||
return Admin::translate('PLUGIN_ADMIN.NOT_VALID_GRAV_PACKAGE');
|
||||
}
|
||||
|
||||
if ($type == 'grav') {
|
||||
Installer::isValidDestination(GRAV_ROOT . '/system');
|
||||
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 {
|
||||
$name = GravGPM::getPackageName($extracted);
|
||||
|
||||
if (!$name) {
|
||||
return "Name could not be determined";
|
||||
return Admin::translate('PLUGIN_ADMIN.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";
|
||||
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);
|
||||
|
||||
|
||||
if(Installer::lastErrorCode()) {
|
||||
if (Installer::lastErrorCode()) {
|
||||
return Installer::lastErrorMsg();
|
||||
}
|
||||
|
||||
} else {
|
||||
return "ZIP package could not be found";
|
||||
return Admin::translate('PLUGIN_ADMIN.ZIP_PACKAGE_NOT_FOUND');
|
||||
}
|
||||
|
||||
Folder::delete($tmp_zip);
|
||||
@@ -276,14 +281,11 @@ class Gpm
|
||||
$query = '';
|
||||
|
||||
if ($package->premium) {
|
||||
$query = \json_encode(array_merge(
|
||||
$package->premium,
|
||||
[
|
||||
$query = \json_encode(array_merge($package->premium, [
|
||||
'slug' => $package->slug,
|
||||
'filename' => $package->premium['filename'],
|
||||
'license_key' => $license
|
||||
]
|
||||
));
|
||||
]));
|
||||
|
||||
$query = '?d=' . base64_encode($query);
|
||||
}
|
||||
@@ -297,9 +299,7 @@ class Gpm
|
||||
$tmp_dir = Admin::getTempDir() . '/Grav-' . uniqid();
|
||||
Folder::mkdir($tmp_dir);
|
||||
|
||||
$bad_chars = array_merge(
|
||||
array_map('chr', range(0, 31)),
|
||||
array("<", ">", ":", '"', "/", "\\", "|", "?", "*"));
|
||||
$bad_chars = array_merge(array_map('chr', range(0, 31)), ["<", ">", ":", '"', "/", "\\", "|", "?", "*"]);
|
||||
|
||||
$filename = $package->slug . str_replace($bad_chars, "", basename($package->zipball_url));
|
||||
|
||||
@@ -341,12 +341,11 @@ class Gpm
|
||||
}
|
||||
|
||||
if (method_exists($upgrader, 'meetsRequirements') && !$upgrader->meetsRequirements()) {
|
||||
$error = [];
|
||||
$error = [];
|
||||
$error[] = '<p>Grav has increased the minimum PHP requirement.<br />';
|
||||
$error[] = 'You are currently running PHP <strong>' . PHP_VERSION . '</strong>';
|
||||
$error[] = ', but PHP <strong>' . GRAV_PHP_MIN . '</strong> is required.</p>';
|
||||
$error[] =
|
||||
'<p><a href="http://getgrav.org/blog/changing-php-requirements-to-5.5" class="button button-small secondary">Additional information</a></p>';
|
||||
$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));
|
||||
|
||||
@@ -354,11 +353,10 @@ class Gpm
|
||||
}
|
||||
|
||||
$update = $upgrader->getAssets()['grav-update'];
|
||||
$tmp = Admin::getTempDir() . '/Grav-' . uniqid();
|
||||
$file = self::_downloadSelfupgrade($update, $tmp);
|
||||
$tmp = Admin::getTempDir() . '/Grav-' . uniqid();
|
||||
$file = self::_downloadSelfupgrade($update, $tmp);
|
||||
|
||||
Installer::install($file, GRAV_ROOT,
|
||||
['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true]);
|
||||
Installer::install($file, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true]);
|
||||
|
||||
$errorCode = Installer::lastErrorCode();
|
||||
|
||||
|
||||
@@ -644,4 +644,14 @@ PLUGIN_ADMIN:
|
||||
ERROR_REINSTALLING_THE: "Error reinstalling the %s"
|
||||
PACKAGE_X_REINSTALLED_SUCCESSFULLY: "Package %s reinstalled successfully"
|
||||
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
|
||||
---
|
||||
2
themes/grav/css-compiled/template.css
vendored
2
themes/grav/css-compiled/template.css
vendored
@@ -3980,4 +3980,4 @@ button.toast-close-button {
|
||||
#admin-main #notifications .badge.alert i, #admin-main #notifications .sidebar-open #admin-sidebar #admin-menu li .badges .alert.updates i, .sidebar-open #admin-sidebar #admin-menu li .badges #admin-main #notifications .alert.updates i, #admin-main #notifications .gpm .alert.gpm-testing i, .gpm #admin-main #notifications .alert.gpm-testing i {
|
||||
margin-right: 3px; }
|
||||
|
||||
/*# sourceMappingURL=../css-compiled/template.css.map */
|
||||
/*# sourceMappingURL=../css-compiled/template.css.map */
|
||||
|
||||
@@ -1086,3 +1086,14 @@ body.sidebar-quickopen #admin-main {
|
||||
.pointer-events-disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Direct install
|
||||
|
||||
.direct-install-content {
|
||||
padding: 30px;
|
||||
|
||||
.button {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{% if authorize(['admin.login', 'admin.super']) %}
|
||||
<nav id="admin-sidebar" data-quickopen="{{ config.plugins.admin.sidebar.activate == 'hover' ? 'true' : 'false' }}" data-quickopen-delay="{{ config.plugins.admin.sidebar.hover_delay }}">
|
||||
{% 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 %}
|
||||
|
||||
<div id="admin-logo">
|
||||
{% include 'partials/logo.html.twig' %}
|
||||
</div>
|
||||
@@ -24,9 +25,9 @@
|
||||
<a href="{{ base_url_relative }}/pages">
|
||||
<i class="fa fa-fw fa-file-text-o"></i>
|
||||
<em>{{ "PLUGIN_ADMIN.PAGES"|tu }}</em>
|
||||
<span class="badges">
|
||||
<span class="badge count">{{ admin.pagesCount }}</span>
|
||||
</span>
|
||||
<span class="badges">
|
||||
<span class="badge count">{{ admin.pagesCount }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@@ -47,7 +48,7 @@
|
||||
<a href="{{ base_url_relative }}/plugins">
|
||||
<i class="fa fa-fw fa-plug"></i>
|
||||
<em>{{ "PLUGIN_ADMIN.PLUGINS"|tu }}</em>
|
||||
<span class="badges">
|
||||
<span class="badges">
|
||||
<span class="badge updates"></span>
|
||||
<span class="badge count">{{ admin.plugins|length }}</span>
|
||||
</span>
|
||||
@@ -59,10 +60,18 @@
|
||||
<a href="{{ base_url_relative }}/themes">
|
||||
<i class="fa fa-fw fa-tint"></i>
|
||||
<em>{{ "PLUGIN_ADMIN.THEMES"|tu }}</em>
|
||||
<span class="badges">
|
||||
<span class="badge updates"></span>
|
||||
<span class="badge count">{{ admin.themes|length }}</span>
|
||||
</span>
|
||||
<span class="badges">
|
||||
<span class="badge updates"></span>
|
||||
<span class="badge count">{{ admin.themes|length }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% 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 %}
|
||||
|
||||
@@ -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