first commit

This commit is contained in:
2018-04-11 17:57:57 +02:00
commit fdfcfbd2a6
4073 changed files with 501866 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
<?php
namespace Grav\Plugin\Admin;
use BaconQrCode\Renderer\Image\Png as BaconPng;
use BaconQrCode\Writer as BaconWriter;
use RobThree\Auth\Providers\Qr\IQRCodeProvider;
class BaconQRProvider implements IQRCodeProvider
{
public function getMimeType()
{
return 'image/png';
}
public function getQRCodeImage($qrtext, $size = 256)
{
$renderer = new BaconPng();
$renderer->setHeight($size);
$renderer->setWidth($size);
$writer = new BaconWriter($renderer);
$result = $writer->writeString($qrtext);
return $result;
}
}
+385
View File
@@ -0,0 +1,385 @@
<?php
namespace Grav\Plugin\Admin;
use Grav\Common\Grav;
use Grav\Common\GPM\GPM as GravGPM;
use Grav\Common\GPM\Licenses;
use Grav\Common\GPM\Installer;
use Grav\Common\GPM\Response;
use Grav\Common\GPM\Upgrader;
use Grav\Common\Filesystem\Folder;
use Grav\Common\GPM\Common\Package;
use Grav\Plugin\Admin\Admin;
/**
* Class Gpm
*
* @package Grav\Plugin\Admin
*/
class Gpm
{
// Probably should move this to Grav DI container?
/** @var GravGPM */
protected static $GPM;
public static function GPM()
{
if (!static::$GPM) {
static::$GPM = new GravGPM();
if (method_exists('GravGPM', 'loadRemoteGrav')) {
static::$GPM->loadRemoteGrav();
}
}
return static::$GPM;
}
/**
* Default options for the install
*
* @var array
*/
protected static $options = [
'destination' => GRAV_ROOT,
'overwrite' => true,
'ignore_symlinks' => true,
'skip_invalid' => true,
'install_deps' => true,
'theme' => false
];
/**
* @param Package[]|string[]|string $packages
* @param array $options
*
* @return bool
*/
public static function install($packages, array $options)
{
$options = array_merge(self::$options, $options);
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);
$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 = '';
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);
$local = static::download($package, $license);
Installer::install($local, $options['destination'],
['install_path' => $package->install_path, 'theme' => $options['theme']]);
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;
}
}
}
return $messages ?: true;
}
/**
* @param Package[]|string[]|string $packages
* @param array $options
*
* @return bool
*/
public static function update($packages, array $options)
{
$options['overwrite'] = true;
return static::install($packages, $options);
}
/**
* @param Package[]|string[]|string $packages
* @param array $options
*
* @return bool
*/
public static function uninstall($packages, array $options)
{
$options = array_merge(self::$options, $options);
$packages = is_array($packages) ? $packages : [$packages];
$count = count($packages);
$packages = array_filter(array_map(function ($p) {
if (is_string($p)) {
$p = strtolower($p);
$plugin = static::GPM()->getInstalledPlugin($p);
$p = $plugin ?: static::GPM()->getInstalledTheme($p);
}
return $p instanceof Package ? $p : false;
}, $packages));
if (!$options['skip_invalid'] && $count !== count($packages)) {
return false;
}
foreach ($packages as $package) {
$location = Grav::instance()['locator']->findResource($package->package_type . '://' . $package->slug);
// 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;
}
}
}
return true;
}
/**
* 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();
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) {
Folder::delete($tmp_source);
Folder::delete($tmp_zip);
return Admin::translate('PLUGIN_ADMIN.PACKAGE_EXTRACTION_FAILED');
}
$type = GravGPM::getPackageType($extracted);
if (!$type) {
Folder::delete($tmp_source);
Folder::delete($tmp_zip);
return Admin::translate('PLUGIN_ADMIN.NOT_VALID_GRAV_PACKAGE');
}
if ($type == 'grav') {
Installer::isValidDestination(GRAV_ROOT . '/system');
if (Installer::IS_LINK === Installer::lastErrorCode()) {
Folder::delete($tmp_source);
Folder::delete($tmp_zip);
return Admin::translate('PLUGIN_ADMIN.CANNOT_OVERWRITE_SYMLINKS');
}
Installer::install($zip, GRAV_ROOT,
['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true, 'ignores' => ['tmp','user','vendor']], $extracted);
} else {
$name = GravGPM::getPackageName($extracted);
if (!$name) {
Folder::delete($tmp_source);
Folder::delete($tmp_zip);
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) {
Folder::delete($tmp_source);
Folder::delete($tmp_zip);
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);
}
Folder::delete($tmp_source);
if (Installer::lastErrorCode()) {
return Installer::lastErrorMsg();
}
} else {
return Admin::translate('PLUGIN_ADMIN.ZIP_PACKAGE_NOT_FOUND');
}
Folder::delete($tmp_zip);
return true;
}
/**
* @param Package $package
*
* @return string
*/
private static function download(Package $package, $license = null)
{
$query = '';
if ($package->premium) {
$query = \json_encode(array_merge($package->premium, [
'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());
}
$tmp_dir = Admin::getTempDir() . '/Grav-' . uniqid();
Folder::mkdir($tmp_dir);
$bad_chars = array_merge(array_map('chr', range(0, 31)), ["<", ">", ":", '"', "/", "\\", "|", "?", "*"]);
$filename = $package->slug . str_replace($bad_chars, "", basename($package->zipball_url));
$filename = preg_replace('/[\\\\\/:"*?&<>|]+/mi', '-', $filename);
file_put_contents($tmp_dir . DS . $filename . '.zip', $contents);
return $tmp_dir . DS . $filename . '.zip';
}
/**
* @param array $package
* @param string $tmp
*
* @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);
return $tmp . DS . $package['name'];
}
/**
* @return bool
*/
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);
return false;
}
if (method_exists($upgrader, 'meetsRequirements') && !$upgrader->meetsRequirements()) {
$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>';
Installer::setError(implode("\n", $error));
return false;
}
$update = $upgrader->getAssets()['grav-update'];
$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;
}
}
+295
View File
@@ -0,0 +1,295 @@
<?php
namespace Grav\Plugin\Admin;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Common\Page\Page;
/**
* Class Popularity
* @package Grav\Plugin
*/
class Popularity
{
/** @var Config */
protected $config;
protected $data_path;
protected $daily_file;
protected $monthly_file;
protected $totals_file;
protected $visitors_file;
protected $daily_data;
protected $monthly_data;
protected $totals_data;
protected $visitors_data;
const DAILY_FORMAT = 'd-m-Y';
const MONTHLY_FORMAT = 'm-Y';
const DAILY_FILE = 'daily.json';
const MONTHLY_FILE = 'monthly.json';
const TOTALS_FILE = 'totals.json';
const VISITORS_FILE = 'visitors.json';
public function __construct()
{
$this->config = Grav::instance()['config'];
$this->data_path = Grav::instance()['locator']->findResource('log://popularity', true, true);
$this->daily_file = $this->data_path . '/' . self::DAILY_FILE;
$this->monthly_file = $this->data_path . '/' . self::MONTHLY_FILE;
$this->totals_file = $this->data_path . '/' . self::TOTALS_FILE;
$this->visitors_file = $this->data_path . '/' . self::VISITORS_FILE;
}
public function trackHit()
{
// Don't track bot or crawler requests
if (!Grav::instance()['browser']->isHuman()) {
return;
}
/** @var Page $page */
$page = Grav::instance()['page'];
$relative_url = str_replace(Grav::instance()['base_url_relative'], '', $page->url());
// Don't track error pages or pages that have no route
if ($page->template() == 'error' || !$page->route()) {
return;
}
// Make sure no 'widcard-style' ignore matches this url
foreach ((array)$this->config->get('plugins.admin.popularity.ignore') as $ignore) {
if (fnmatch($ignore, $relative_url)) {
return;
}
}
// initial creation if it doesn't exist
if (!file_exists($this->data_path)) {
mkdir($this->data_path);
$this->flushPopularity();
}
// Update the data we want to track
$this->updateDaily();
$this->updateMonthly();
$this->updateTotals($page->route());
$this->updateVisitors(Grav::instance()['uri']->ip());
}
protected function updateDaily()
{
if (!$this->daily_data) {
$this->daily_data = $this->getData($this->daily_file);
}
$day_month_year = date(self::DAILY_FORMAT);
// get the daily access count
if (array_key_exists($day_month_year, $this->daily_data)) {
$this->daily_data[$day_month_year] = intval($this->daily_data[$day_month_year]) + 1;
} else {
$this->daily_data[$day_month_year] = 1;
}
// keep correct number as set by history
$count = intval($this->config->get('plugins.admin.popularity.history.daily', 30));
$total = count($this->daily_data);
if ($total > $count) {
$this->daily_data = array_slice($this->daily_data, -$count, $count, true);
}
file_put_contents($this->daily_file, json_encode($this->daily_data));
}
/**
* @return array
*/
public function getDailyChartData()
{
if (!$this->daily_data) {
$this->daily_data = $this->getData($this->daily_file);
}
$limit = intval($this->config->get('plugins.admin.popularity.dashboard.days_of_stats', 7));
$chart_data = array_slice($this->daily_data, -$limit, $limit);
$labels = [];
$data = [];
foreach ($chart_data as $date => $count) {
$labels[] = Grav::instance()['grav']['admin']->translate([
'PLUGIN_ADMIN.' . strtoupper(date('D', strtotime($date)))]) .
'<br>' . date('M d', strtotime($date));
$data[] = $count;
}
return ['labels' => $labels, 'data' => $data];
}
/**
* @return int
*/
public function getDailyTotal()
{
if (!$this->daily_data) {
$this->daily_data = $this->getData($this->daily_file);
}
if (isset($this->daily_data[date(self::DAILY_FORMAT)])) {
return $this->daily_data[date(self::DAILY_FORMAT)];
} else {
return 0;
}
}
/**
* @return int
*/
public function getWeeklyTotal()
{
if (!$this->daily_data) {
$this->daily_data = $this->getData($this->daily_file);
}
$day = 0;
$total = 0;
foreach (array_reverse($this->daily_data) as $daily) {
$total += $daily;
$day++;
if ($day == 7) {
break;
}
}
return $total;
}
/**
* @return int
*/
public function getMonthlyTotal()
{
if (!$this->monthly_data) {
$this->monthly_data = $this->getData($this->monthly_file);
}
if (isset($this->monthly_data[date(self::MONTHLY_FORMAT)])) {
return $this->monthly_data[date(self::MONTHLY_FORMAT)];
} else {
return 0;
}
}
protected function updateMonthly()
{
if (!$this->monthly_data) {
$this->monthly_data = $this->getData($this->monthly_file);
}
$month_year = date(self::MONTHLY_FORMAT);
// get the monthly access count
if (array_key_exists($month_year, $this->monthly_data)) {
$this->monthly_data[$month_year] = intval($this->monthly_data[$month_year]) + 1;
} else {
$this->monthly_data[$month_year] = 1;
}
// keep correct number as set by history
$count = intval($this->config->get('plugins.admin.popularity.history.monthly', 12));
$total = count($this->monthly_data);
$this->monthly_data = array_slice($this->monthly_data, $total - $count, $count);
file_put_contents($this->monthly_file, json_encode($this->monthly_data));
}
/**
* @return array
*/
protected function getMonthyChartData()
{
if (!$this->monthly_data) {
$this->monthly_data = $this->getData($this->monthly_file);
}
$labels = [];
$data = [];
foreach ($this->monthly_data as $date => $count) {
$labels[] = date('M', strtotime($date));
$data[] = $count;
}
return ['labels' => $labels, 'data' => $data];
}
/**
* @param string $url
*/
protected function updateTotals($url)
{
if (!$this->totals_data) {
$this->totals_data = $this->getData($this->totals_file);
}
// get the totals for this url
if (array_key_exists($url, $this->totals_data)) {
$this->totals_data[$url] = intval($this->totals_data[$url]) + 1;
} else {
$this->totals_data[$url] = 1;
}
file_put_contents($this->totals_file, json_encode($this->totals_data));
}
/**
* @param string $ip
*/
protected function updateVisitors($ip)
{
if (!$this->visitors_data) {
$this->visitors_data = $this->getData($this->visitors_file);
}
// update with current timestamp
$this->visitors_data[$ip] = time();
$visitors = $this->visitors_data;
arsort($visitors);
$count = intval($this->config->get('plugins.admin.popularity.history.visitors', 20));
$this->visitors_data = array_slice($visitors, 0, $count, true);
file_put_contents($this->visitors_file, json_encode($this->visitors_data));
}
/**
* @param string $path
*
* @return array
*/
protected function getData($path)
{
if (file_exists($path)) {
return (array)json_decode(file_get_contents($path), true);
} else {
return [];
}
}
public function flushPopularity()
{
file_put_contents($this->daily_file, []);
file_put_contents($this->monthly_file, []);
file_put_contents($this->totals_file, []);
file_put_contents($this->visitors_file, []);
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace Grav\Plugin\Admin;
/**
* Admin theme object
*
* @author RocketTheme
* @license MIT
*/
class Themes extends \Grav\Common\Themes
{
public function init()
{
/** @var Themes $themes */
$themes = $this->grav['themes'];
$themes->configure();
$themes->initTheme();
$this->grav->fireEvent('onAdminThemeInitialized');
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace Grav\Plugin\Admin;
use Grav\Common\Grav;
use Grav\Common\User\User;
/**
* Admin utils class
*
* @license MIT
*/
class Utils
{
/**
* Matches an email to a user
*
* @param $email
*
* @return User
*/
public static function findUserByEmail($email)
{
$account_dir = Grav::instance()['locator']->findResource('account://');
$files = array_diff(scandir($account_dir), ['.', '..']);
foreach ($files as $file) {
if (strpos($file, '.yaml') !== false) {
$user = User::load(trim(substr($file, 0, -5)));
if ($user['email'] == $email) {
return $user;
}
}
}
// If a User with the provided email cannot be found, then load user with that email as the username
return User::load($email);
}
/**
* Generates a slug of the given string
*
* @param string $str
* @return string
*/
public static function slug($str)
{
if (function_exists('transliterator_transliterate')) {
$str = transliterator_transliterate('Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:Punctuation:] Remove;', $str);
} else {
$str = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $str);
}
$str = strtolower($str);
$str = preg_replace('/[-\s]+/', '-', $str);
$str = preg_replace('/[^a-z0-9-]/i', '', $str);
$str = trim($str, '-');
return $str;
}
}