All changes as implemented over the last 3 years.

Pushes Administration into a workable state.
This commit is contained in:
Abel Hoogeveen 2023-02-08 12:57:10 +01:00
parent 50608c18e7
commit 63ea659d5b
38 changed files with 2396 additions and 1232 deletions

26
Dockerfile Normal file → Executable file
View File

@ -1,16 +1,12 @@
FROM php:7.3-apache FROM registry.i15.nl/i15/fuzephp:8.1-apache
MAINTAINER i15 <abel@i15.nl> MAINTAINER i15 <abel@i15.nl>
# Install PHP Deps # Write application to image
# First PDO and MySQL ENV APACHE_DOCUMENT_ROOT /usr/src/admin/www
RUN docker-php-ext-install mysqli pdo_mysql opcache RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
# Write application to image RUN a2enmod rewrite
ENV APACHE_DOCUMENT_ROOT /usr/src/admin/www
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf # Amend permissions
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf COPY --chown=www-data . /usr/src/admin
RUN a2enmod rewrite
# Amend permissions
COPY --chown=www-data . /usr/src/admin
RUN chmod -R 777 /usr/src/admin/test/temp RUN chmod -R 777 /usr/src/admin/test/temp

40
LICENSE Normal file → Executable file
View File

@ -1,21 +1,21 @@
MIT License MIT License
Copyright (c) 2013-2020 i15 Copyright (c) 2013-2023 i15
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

58
composer.json Normal file → Executable file
View File

@ -1,32 +1,28 @@
{ {
"name": "fuzeworks/administration", "name": "fuzeworks/administration",
"description": "description", "description": "Administration interface for FuzeWorks",
"minimum-stability": "stable", "license": ["MIT"],
"license": ["MIT"], "authors": [
"authors": [ {
{ "name": "Abel Hoogeveen",
"name": "Abel Hoogeveen", "email": "abel@i15.nl"
"email": "abel@i15.nl" }
} ],
], "require": {
"require": { "php": ">=8.1.0",
"php": ">=7.2.0", "fuzeworks/webcomponent": "~1.3.0",
"fuzeworks/core": "~1.2", "fuzeworks/authentication": "~1.3.0",
"fuzeworks/mvcr": "~1.2", "fuzeworks/layout": "~1.3.0",
"fuzeworks/webcomponent": "~1.2", "latte/latte": "~2.5",
"fuzeworks/layout": "~1.2", "almasaeed2010/adminlte": "^3"
"fuzeworks/database": "~1.2", },
"latte/latte": "^2", "require-dev": {
"almasaeed2010/adminlte": "^3", "fuzeworks/tracycomponent": "~1.3.0",
"phpdocumentor/reflection-docblock": "^5" "phpunit/phpunit": "^9"
}, },
"require-dev": { "autoload": {
"phpunit/phpunit": "^9", "psr-4": {
"fuzeworks/tracycomponent": "~1.2" "FuzeWorks\\Administration\\": "src/FuzeWorks/Administration/"
}, }
"autoload": { }
"psr-4": {
"FuzeWorks\\Administration\\": "src/FuzeWorks/Administration/"
}
}
} }

74
config.admin.php Normal file → Executable file
View File

@ -1,38 +1,38 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
return [ return [
'admin_enabled' => true, 'admin_enabled' => true,
'admin_url' => 'admin' 'admin_url' => 'admin'
]; ];

View File

@ -1,10 +1,10 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * i15Site.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2022 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -25,18 +25,18 @@
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2022, i15. (http://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @link https://i15.nl
* @since Version 2.0.0
* *
* @version Version 1.3.0 * @version Version 2.0.0
*/ */
namespace Application\Controller; namespace Application\Controller;
use FuzeWorks\WebController; use FuzeWorks\WebController;
class IndexController extends WebController class AsyncController extends WebController
{ {
} }

View File

@ -0,0 +1,36 @@
<?php
namespace Application\Controller;
use FuzeWorks\Factory;
use FuzeWorks\ObjectStorage\ObjectStorageComponent;
use FuzeWorks\WebController;
class CacheController extends WebController
{
public function getCacheItems(int $index = 1, int $pageSize = 50): ?array
{
// Get storage
/** @var ObjectStorageComponent $storage */
$storage = Factory::getInstance("storage");
$store = $storage->getStorage();
// First retrieve the index
$objectIndex = $store->getIndex();
$out = [];
for ($i = $index; $i < ($index + $pageSize); $i++)
{
if (!isset($objectIndex[$i]))
continue;
$out[] = $store->getItem($objectIndex[$i]);
//$out[] = $store->getItemMeta($objectIndex[$i]);
}
return $out;
}
}

View File

@ -1,55 +1,50 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
namespace Application\View; namespace Application\Controller;
use FuzeWorks\Administration\AdminView; use FuzeWorks\Administration\Attributes\DashboardAttribute;
use FuzeWorks\Administration\PageFinder;
class IndexAdminView extends AdminView use FuzeWorks\WebController;
{
class DashboardController extends WebController
/** {
* Hello!
* public function populateDashboard()
* This does a lot of things {
* $finder = new PageFinder();
* @display Dashboard $views = $finder->findViewsWithAttribute(DashboardAttribute::class);
* @icon tachometer-alt }
* @priority Priority::HIGHEST
*/
public function index()
{
}
} }

View File

@ -1,80 +1,130 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
namespace Application\Controller; namespace Application\Controller;
use FuzeWorks\Core;
use FuzeWorks\Priority; use FuzeWorks\Core;
use FuzeWorks\WebController; use FuzeWorks\Exception\NotFoundException;
use FuzeWorks\Priority;
class SettingsController extends WebController use FuzeWorks\WebController;
{
class SettingsController extends WebController
public function findSettingsFiles(): array {
{
// First collect all the known paths public function findSettingsFiles(): array
$paths = []; {
for ($i = Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) // First collect all the known paths
$paths[$i] = $this->config->getComponentPaths($i); $paths = [];
for ($i = Priority::getHighestPriority(); $i <= Priority::getLowestPriority(); $i++)
// And add the CorePath $paths[$i] = $this->config->getComponentPaths($i);
$paths[Priority::getLowestPriority()][] = Core::$coreDir . DS . 'Config';
// Then go over every individual paths
// Then go over every individual paths $configFiles = [];
$configFiles = []; foreach ($paths as $priority => $foldersArray) {
foreach ($paths as $priority => $foldersArray) foreach ($foldersArray as $folder) {
{ // Get the contents from the folder
foreach ($foldersArray as $folder) $contents = array_diff(scandir($folder), array('..', '.'));
{
// Get the contents from the folder // Then go over the folders, and see which ones are an admin view
$contents = array_diff(scandir($folder), array('..', '.')); foreach ($contents as $file) {
// If the file matches the expected filename, add it to the list
// Then go over the folders, and see which ones are an admin view if (str_starts_with($file, 'config.')) {
foreach ($contents as $file) if (!isset($configFiles[$file]))
{ $configFiles[$file] = [];
// If the file matches the expected filename, add it to the list
if (substr($file, 0, 7) === 'config.' && !isset($configFiles[$file])) $configFiles[$file][] = ['folder' => $folder, 'priority' => substr(Priority::getPriority($priority), 10)];
$configFiles[$file] = ['folder' => $folder, 'priority' => substr(Priority::getPriority($priority), 10)]; }
} }
} }
} }
// At the end, sort the files // At the end, sort the files
ksort($configFiles); ksort($configFiles);
// And return them // And return them
return $configFiles; return $configFiles;
} }
/**
* Fetch a file
*
* @param string $fileName
* @return array
* @throws NotFoundException
*/
public function fetchFile(string $fileName, int $selector): array
{
// Fetch files
$files = $this->findSettingsFiles();
// Check if the file exists
if (!isset($files[$fileName]))
throw new NotFoundException("The requested settings file could not be found.");
// Select correct data
$meta = $files[$fileName][$selector];
// Find the file
$file = $meta['folder'] . DS . $fileName;
if (!file_exists($file))
throw new NotFoundException("The requested settings file could not be found.");
// Load the raw data
$raw = file_get_contents($file);
// And try to load the editable data
$data = include($file);
// Prepare output variable
return ['name' => $fileName, 'directory' => $meta['folder'], 'raw' => $raw, 'data' => $data];
}
protected function verifyFileData(array $fileData): bool
{
foreach ($fileData as $key => $val) {
switch (gettype($val)) {
case 'array':
case 'object':
case 'resource':
case 'unknown type':
return false;
break;
}
}
return true;
}
} }

View File

77
layouts/components/settings/layout.overview.latte Normal file → Executable file
View File

@ -1,40 +1,39 @@
<div class="row"> <div class="row">
<div class="col-xl-12"> <div class="col-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Configuration Files</h3> <h3 class="card-title">Configuration Files</h3>
</div> </div>
<!-- /.card-header --> <!-- /.card-header -->
<div class="card-body p-0"> <div class="card-body p-0">
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th style="width: 10px">#</th> <th style="width: 10px">#</th>
<th>Name</th> <th>Name</th>
<th>Folder</th> <th>Folder</th>
<th>Priority</th> <th>Priority</th>
<th style="width: 20px">View</th> <th style="width: 20px">View</th>
<th style="width: 20px">Edit</th> <th style="width: 20px">Edit</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{foreach $files as $fileName => $data} {var $it = 0}
<tr> {foreach $files as $fileName => $data}
<td>{$iterator->getCounter()}</td> <tr n:foreach="$data as $entry">
<td>{$fileName}</td> {var $it = $it + 1}
<td>{$data['folder']}</td> <td>{$it}</td>
<td>{$data['priority']}</td> <td>{$fileName}</td>
<td><button type="button" class="btn btn-block btn-success">VIEW</button></td> <td>{$entry['folder']}</td>
<td><button type="button" class="btn btn-block btn-primary">EDIT</button></td> <td>{$entry['priority']}</td>
</tr> <td><a href="/{$adminKey}/settings/view/{$fileName}/{$iterator->counter0}"><button type="button" class="btn btn-block btn-success">VIEW</button></a></td>
{/foreach} <td><a href="/{$adminKey}/settings/modify/{$fileName}/{$iterator->counter0}"><button type="button" class="btn btn-block btn-primary">EDIT</button></a></td>
</tbody> </tr>
</table> {/foreach}
</div> </tbody>
<!-- /.card-body --> </table>
</div> </div>
</div> <!-- /.card-body -->
</div> </div>
</div>
</div> </div>

View File

@ -0,0 +1,63 @@
{varType FuzeWorks\Forms\Form $form}
{varType FuzeWorks\Forms\Field $field}
<div class="row">
<div class="col-12">
<!-- Custom Tabs -->
<div class="card">
<div class="card-header d-flex p-0">
<h3 class="card-title p-3">{$config['name']}</h3>
<ul class="nav nav-pills ml-auto p-2">
{ifset $config['data']}
<li class="nav-item">
<a class="nav-link active" href="#data" data-toggle="tab">Data</a>
</li>
{/ifset}
<li class="nav-item"><a class="nav-link {$config['data'] ? '' : 'active'}" href="#raw" data-toggle="tab">Raw</a></li>
</ul>
</div><!-- /.card-header -->
<div class="card-body">
<div class="tab-content">
{foreach $form->getFields() as $field}
{switch get_class($field)}
{case "FuzeWorks\Forms\Fields\HiddenField", "FuzeWorks\Forms\Fields\SubmitField"}
{$field|noescape}
{case "FuzeWorks\Forms\Fields\CheckboxField"}
<div class="form-check">
{$field->addClass("form-check-input")|noescape}
<label for="{$field->getId()}" class="form-check-label">{$field->getLabel()}</label>
{if $field->isValidated() && !$field->isValid()}
<small class="text-danger">{$field->getErrors()|implode}</small>
{/if}
</div>
{default}
<div class="form-group">
<label for="{$field->getId()}">{$field->getLabel()}</label>
{if $field->isValidated() && !$field->isValid()}
{$field->class(["form-control", "is-invalid"])|noescape}
<small class="text-danger">{$field->getErrors()|implode}</small>
{elseif $field->isValidated() && $field->isValid()}
{$field->class(["form-control", "is-valid"])|noescape}
{else}
{$field->addClass("form-control")|noescape}
{/if}
</div>
{/switch}
{/foreach}
<div class="tab-pane {$config['data'] ? '' : 'active'}" id="raw">
<textarea disabled style="min-width: 100%; height: 100%" oninput='resizeRawArea(this)'>{$config['raw']}</textarea>
</div>
<!-- /.tab-pane -->
</div>
<!-- /.tab-content -->
</div><!-- /.card-body -->
</div>
<!-- ./card -->
</div>
<!-- /.col -->
</div>
<script>
function resizeRawArea(jq_in) {
jq_in.style.height = "";
jq_in.style.height = jq_in.scrollHeight + 3 + "px";
}
</script>

View File

@ -0,0 +1,12 @@
{varType FuzeWorks\Forms\Form $form}
<div class="col-md-6">
<div class="card card-primary">
<div class="card card-header">
<h3 class="card-title">{$form->getLabel()}</h3>
</div>
<div class="card-body">
{$form|noescape}
</div>
</div>
</div>

View File

@ -0,0 +1,13 @@
<div class="error-page">
<h2 class="headline text-danger">403</h2>
<div class="error-content">
<h3><i class="fas fa-exclamation-triangle text-danger"></i> Unauthorized! </h3>
<p>
You do not have permission to view that page.
Meanwhile, you may <a href="/{$adminKey}">return to dashboard</a>.
</p>
</div>
</div>
<!-- /.error-page -->

View File

@ -0,0 +1,14 @@
<div class="error-page">
<h2 class="headline text-warning"> 404</h2>
<div class="error-content">
<h3><i class="fas fa-exclamation-triangle text-warning"></i> Oops! Page not found.</h3>
<p>
We could not find the page you were looking for.
Meanwhile, you may <a href="/{$adminKey}">return to dashboard</a>.
</p>
</div>
<!-- /.error-content -->
</div>
<!-- /.error-page -->

View File

@ -0,0 +1,13 @@
<div class="error-page">
<h2 class="headline text-danger">500</h2>
<div class="error-content">
<h3><i class="fas fa-exclamation-triangle text-danger"></i> Oops! Something went wrong.</h3>
<p>
We will work on fixing that right away.
Meanwhile, you may <a href="/{$adminKey}">return to dashboard</a>.
</p>
</div>
</div>
<!-- /.error-page -->

426
layouts/main/layout.panel.latte Normal file → Executable file
View File

@ -1,268 +1,158 @@
<!DOCTYPE html> {varType FuzeWorks\Authentication\Model\Session $session}
<html> {varType bool $loadAsync}
<head> <!DOCTYPE html>
<meta charset="utf-8"> <html lang="en">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <head>
<title>AdminLTE 3 | Dashboard</title> <meta charset="utf-8">
<!-- Tell the browser to be responsive to screen width --> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <title>{$serverName} | {$pageTitle}</title>
<!-- Font Awesome --> <!-- Tell the browser to be responsive to screen width -->
<link rel="stylesheet" href="/{$adminKey}/plugins/fontawesome-free/css/all.min.css"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Ionicons --> <!-- Font Awesome -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> <link rel="stylesheet" href="/{$adminKey}/lte_plugins/fontawesome-free/css/all.min.css">
<!-- Tempusdominus Bbootstrap 4 --> <!-- Theme style -->
<link rel="stylesheet" href="/{$adminKey}/plugins/tempusdominus-bootstrap-4/css/tempusdominus-bootstrap-4.min.css"> <link rel="stylesheet" href="/{$adminKey}/lte_dist/css/adminlte.min.css">
<!-- iCheck --> <!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="/{$adminKey}/plugins/icheck-bootstrap/icheck-bootstrap.min.css"> <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
<!-- JQVMap --> </head>
<link rel="stylesheet" href="/{$adminKey}/plugins/jqvmap/jqvmap.min.css"> <body class="hold-transition sidebar-mini layout-fixed">
<!-- Theme style --> <div class="wrapper">
<link rel="stylesheet" href="/{$adminKey}/dist/css/adminlte.min.css">
<!-- overlayScrollbars --> <!-- Navbar -->
<link rel="stylesheet" href="/{$adminKey}/plugins/overlayScrollbars/css/OverlayScrollbars.min.css"> <nav class="main-header navbar navbar-expand navbar-white navbar-light">
<!-- Daterange picker --> <!-- Left navbar links -->
<link rel="stylesheet" href="/{$adminKey}/plugins/daterangepicker/daterangepicker.css"> <ul class="navbar-nav">
<!-- summernote --> <li class="nav-item">
<link rel="stylesheet" href="/{$adminKey}/plugins/summernote/summernote-bs4.css"> <a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
<!-- Google Font: Source Sans Pro --> </li>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet"> <li class="nav-item d-none d-sm-inline-block">
</head> <a href="/{$adminKey}" class="nav-link">Dashboard</a>
<body class="hold-transition sidebar-mini layout-fixed"> </li>
<div class="wrapper"> </ul>
<!-- Navbar --> <!-- Right navbar links -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light"> <ul class="navbar-nav ml-auto">
<!-- Left navbar links --> {if $loadAsync}
<ul class="navbar-nav"> <li id="asyncWidget" class="nav-item dropdown">
<li class="nav-item"> <a class="nav-link" data-toggle="dropdown" href="#">
<a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a> <i class="fa fa-list"></i>
</li> <span class="badge badge-warning navbar-badge">0</span>
<li class="nav-item d-none d-sm-inline-block"> </a>
<a href="/" class="nav-link">Home</a> <div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
</li> <span class="dropdown-item dropdown-header">Asynchronous tasks</span>
</ul> <div class="dropdown-divider"></div>
<div class="dropdown-divider"></div>
<!-- SEARCH FORM --> <a href="/{$adminKey}/async" class="dropdown-item dropdown-footer">See all historic tasks</a>
<form class="form-inline ml-3"> </div>
<div class="input-group input-group-sm"> </li>
<input class="form-control form-control-navbar" type="search" placeholder="Search" aria-label="Search"> {/if}
<div class="input-group-append"> <li class="nav-item dropdown user-menu">
<button class="btn btn-navbar" type="submit"> <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">
<i class="fas fa-search"></i> <img src="/{$adminKey}/lte_dist/img/user2-160x160.jpg" class="user-image img-circle elevation-2" alt="User Image">
</button> <span class="d-none d-md-inline">{$session->user->primaryEmail}</span>
</div> </a>
</div> <ul class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
</form> <!-- User image -->
<li class="user-header bg-primary">
<!-- Right navbar links --> <img src="/{$adminKey}/lte_dist/img/user2-160x160.jpg" class="img-circle elevation-2" alt="User Image">
<ul class="navbar-nav ml-auto"> <p>
<!-- Messages Dropdown Menu --> {$session->user->primaryEmail}
<li class="nav-item dropdown"> <small>Member since -</small>
<a class="nav-link" data-toggle="dropdown" href="#"> </p>
<i class="far fa-comments"></i> </li>
<span class="badge badge-danger navbar-badge">3</span> <!-- Menu Footer-->
</a> <li class="user-footer">
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right"> <a href="/{$adminKey}/profile" class="btn btn-default btn-flat">Profile</a>
<a href="#" class="dropdown-item"> <a href="{$authURL}/logout" class="btn btn-default btn-flat float-right">Sign out</a>
<!-- Message Start --> </li>
<div class="media"> </ul>
<img src="/{$adminKey}/dist/img/user1-128x128.jpg" alt="User Avatar" class="img-size-50 mr-3 img-circle"> </li>
<div class="media-body"> </ul>
<h3 class="dropdown-item-title"> </nav>
Brad Diesel <!-- /.navbar -->
<span class="float-right text-sm text-danger"><i class="fas fa-star"></i></span>
</h3> <!-- Main Sidebar Container -->
<p class="text-sm">Call me whenever you can...</p> <aside class="main-sidebar sidebar-dark-primary elevation-4">
<p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p> <!-- Brand Logo -->
</div> <a href="/" class="brand-link">
</div> <img src="/{$adminKey}/lte_dist/img/AdminLTELogo.png" alt="AdminLTE Logo" class="brand-image img-circle elevation-3"
<!-- Message End --> style="opacity: .8">
</a> <span class="brand-text font-weight-light">{$serverName}</span>
<div class="dropdown-divider"></div> </a>
<a href="#" class="dropdown-item">
<!-- Message Start --> <!-- Sidebar -->
<div class="media"> <div class="sidebar">
<img src="/{$adminKey}/dist/img/user8-128x128.jpg" alt="User Avatar" class="img-size-50 img-circle mr-3"> <!-- Sidebar Menu -->
<div class="media-body"> <nav class="mt-2">
<h3 class="dropdown-item-title"> <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
John Pierce {foreach $sidebar as $item}
<span class="float-right text-sm text-muted"><i class="fas fa-star"></i></span> <li class="nav-item">
</h3> <a href="/{$adminKey}/{$item['url']}" class="nav-link">
<p class="text-sm">I got your message bro</p> <i class="nav-icon fas fa-{$item['icon']}"></i>
<p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p> <p>{$item['display']}</p>
</div> </a>
</div> </li>
<!-- Message End --> {/foreach}
</a> </ul>
<div class="dropdown-divider"></div> </nav>
<a href="#" class="dropdown-item"> <!-- /.sidebar-menu -->
<!-- Message Start --> </div>
<div class="media"> <!-- /.sidebar -->
<img src="/{$adminKey}/dist/img/user3-128x128.jpg" alt="User Avatar" class="img-size-50 img-circle mr-3"> </aside>
<div class="media-body">
<h3 class="dropdown-item-title"> <!-- Content Wrapper. Contains page content -->
Nora Silvester <div class="content-wrapper">
<span class="float-right text-sm text-warning"><i class="fas fa-star"></i></span> <!-- Content Header (Page header) -->
</h3> <div class="content-header">
<p class="text-sm">The subject goes here</p> <div class="container-fluid">
<p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p> <div class="row mb-2">
</div> <div class="col-sm-6">
</div> <h1 class="m-0 text-dark">{$pageTitle}</h1>
<!-- Message End --> </div><!-- /.col -->
</a> <div class="col-sm-6">
<div class="dropdown-divider"></div> <ol class="breadcrumb float-sm-right">
<a href="#" class="dropdown-item dropdown-footer">See All Messages</a> <li class="breadcrumb-item"><a href="/{$adminKey}">Home</a></li>
</div> <li class="breadcrumb-item active">{$pageTitle}</li>
</li> </ol>
<!-- Notifications Dropdown Menu --> </div><!-- /.col -->
<li class="nav-item dropdown"> </div><!-- /.row -->
<a class="nav-link" data-toggle="dropdown" href="#"> </div><!-- /.container-fluid -->
<i class="far fa-bell"></i> </div>
<span class="badge badge-warning navbar-badge">15</span> <!-- /.content-header -->
</a>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right"> <!-- Main content -->
<span class="dropdown-item dropdown-header">15 Notifications</span> <section class="content">
<div class="dropdown-divider"></div> <div class="container-fluid">
<a href="#" class="dropdown-item"> {$content|noescape}
<i class="fas fa-envelope mr-2"></i> 4 new messages </div><!-- /.container-fluid -->
<span class="float-right text-muted text-sm">3 mins</span> </section>
</a> <!-- /.content -->
<div class="dropdown-divider"></div> </div>
<a href="#" class="dropdown-item"> <!-- /.content-wrapper -->
<i class="fas fa-users mr-2"></i> 8 friend requests <footer class="main-footer">
<span class="float-right text-muted text-sm">12 hours</span> <strong>Copyright &copy; 2013-{date('Y')} <a href="https://i15.nl">i15.nl</a>.</strong>
</a> All rights reserved.
<div class="dropdown-divider"></div> <div class="float-right d-none d-sm-inline-block">
<a href="#" class="dropdown-item"> <b>Version</b> 1.3.0
<i class="fas fa-file mr-2"></i> 3 new reports </div>
<span class="float-right text-muted text-sm">2 days</span> </footer>
</a> </div>
<div class="dropdown-divider"></div> <!-- ./wrapper -->
<a href="#" class="dropdown-item dropdown-footer">See All Notifications</a>
</div> <!-- jQuery -->
</li> <script src="/{$adminKey}/lte_plugins/jquery/jquery.min.js"></script>
<li class="nav-item"> <!-- Bootstrap 4 -->
<a class="nav-link" data-widget="control-sidebar" data-slide="true" href="#" role="button"> <script src="/{$adminKey}/lte_plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<i class="fas fa-th-large"></i> <!-- AdminLTE App -->
</a> <script src="/{$adminKey}/lte_dist/js/adminlte.js"></script>
</li> {if $loadAsync}
</ul> <script src="/{$adminKey}/admin_dist/async.ts"></script>
</nav> {/if}
<!-- /.navbar --> <script>
$.widget.bridge('uibutton', $.ui.button);
<!-- Main Sidebar Container --> </script>
<aside class="main-sidebar sidebar-dark-primary elevation-4"> {ifset $footer}
<!-- Brand Logo --> {$footer|noescape}
<a href="index3.html" class="brand-link"> {/ifset}
<img src="/{$adminKey}/dist/img/AdminLTELogo.png" alt="AdminLTE Logo" class="brand-image img-circle elevation-3" </body>
style="opacity: .8"> </html>
<span class="brand-text font-weight-light">AdminLTE 3</span>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<img src="/{$adminKey}/dist/img/user2-160x160.jpg" class="img-circle elevation-2" alt="User Image">
</div>
<div class="info">
<a href="#" class="d-block">Juris LLM</a>
</div>
</div>
<!-- Sidebar Menu -->
<nav class="mt-2">
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
{foreach $sidebar as $item}
<li class="nav-item">
<a href="/{$adminKey}/{$item['url']}" class="nav-link">
<i class="nav-icon fas fa-{$item['icon']}"></i>
<p>{$item['display']}</p>
</a>
</li>
{/foreach}
</ul>
</nav>
<!-- /.sidebar-menu -->
</div>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">Dashboard</h1>
</div><!-- /.col -->
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">Dashboard v1</li>
</ol>
</div><!-- /.col -->
</div><!-- /.row -->
</div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
<!-- Main content -->
<section class="content">
<div class="container-fluid">
{$content|noescape}
</div><!-- /.container-fluid -->
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
<footer class="main-footer">
<strong>Copyright &copy; 2013-2020 <a href="https://i15.nl">i15.nl</a>.</strong>
All rights reserved.
<div class="float-right d-none d-sm-inline-block">
<b>Version</b> 1.3.0
</div>
</footer>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
</aside>
<!-- /.control-sidebar -->
</div>
<!-- ./wrapper -->
<!-- jQuery -->
<script src="/{$adminKey}/plugins/jquery/jquery.min.js"></script>
<!-- jQuery UI 1.11.4 -->
<script src="/{$adminKey}/plugins/jquery-ui/jquery-ui.min.js"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button)
</script>
<!-- Bootstrap 4 -->
<script src="/{$adminKey}/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- ChartJS -->
<script src="/{$adminKey}/plugins/chart.js/Chart.min.js"></script>
<!-- Sparkline -->
<script src="/{$adminKey}/plugins/sparklines/sparkline.js"></script>
<!-- JQVMap -->
<script src="/{$adminKey}/plugins/jqvmap/jquery.vmap.min.js"></script>
<script src="/{$adminKey}/plugins/jqvmap/maps/jquery.vmap.usa.js"></script>
<!-- jQuery Knob Chart -->
<script src="/{$adminKey}/plugins/jquery-knob/jquery.knob.min.js"></script>
<!-- daterangepicker -->
<script src="/{$adminKey}/plugins/moment/moment.min.js"></script>
<script src="/{$adminKey}/plugins/daterangepicker/daterangepicker.js"></script>
<!-- Tempusdominus Bootstrap 4 -->
<script src="/{$adminKey}/plugins/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js"></script>
<!-- Summernote -->
<script src="/{$adminKey}/plugins/summernote/summernote-bs4.min.js"></script>
<!-- overlayScrollbars -->
<script src="/{$adminKey}/plugins/overlayScrollbars/js/jquery.overlayScrollbars.min.js"></script>
<!-- AdminLTE App -->
<script src="/{$adminKey}/dist/js/adminlte.js"></script>
</body>
</html>

630
src/FuzeWorks/Administration/AdminPlugin.php Normal file → Executable file
View File

@ -1,212 +1,420 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
namespace FuzeWorks\Administration; namespace FuzeWorks\Administration;
use FuzeWorks\Config; use FuzeWorks\Administration\Attributes\DisplayAttribute;
use FuzeWorks\ConfigORM\ConfigORM; use FuzeWorks\Administration\Attributes\FooterCodeMethodAttribute;
use FuzeWorks\Controllers; use FuzeWorks\Administration\Attributes\IconAttribute;
use FuzeWorks\Event\LayoutLoadEvent; use FuzeWorks\Administration\Attributes\PermissionAttribute;
use FuzeWorks\Event\RouteWebRequestEvent; use FuzeWorks\Authentication\AuthenticationPlugin;
use FuzeWorks\Events; use FuzeWorks\Authentication\Model\Session;
use FuzeWorks\Factory; use FuzeWorks\Config;
use FuzeWorks\Layout; use FuzeWorks\ConfigORM\ConfigORM;
use FuzeWorks\Logger; use FuzeWorks\Controllers;
use FuzeWorks\Models; use FuzeWorks\Core;
use FuzeWorks\Priority; use FuzeWorks\Event\LayoutLoadEvent;
use FuzeWorks\Resources; use FuzeWorks\Event\RouterCallViewEvent;
use FuzeWorks\Router; use FuzeWorks\Event\RouteWebRequestEvent;
use FuzeWorks\Views; use FuzeWorks\Events;
use FuzeWorks\Exception\ConfigException;
class AdminPlugin implements \FuzeWorks\iPluginHeader use FuzeWorks\Exception\EventException;
{ use FuzeWorks\Exception\Exception;
use FuzeWorks\Exception\FactoryException;
private $pluginKey = 'admin'; use FuzeWorks\Exception\HaltException;
use FuzeWorks\Exception\LayoutException;
/** @var Config */ use FuzeWorks\Exception\OutputException;
private $config; use FuzeWorks\Exception\WebException;
use FuzeWorks\Factory;
/** @var Router */ use FuzeWorks\iPluginHeader;
private $router; use FuzeWorks\Layout;
use FuzeWorks\Logger;
/** @var Models */ use FuzeWorks\Models;
private $models; use FuzeWorks\Output;
use FuzeWorks\Priority;
/** @var Views */ use FuzeWorks\Resources;
private $views; use FuzeWorks\Router;
use FuzeWorks\View;
/** @var Controllers */ use FuzeWorks\Views;
private $controllers; use ReflectionClass;
use ReflectionException;
/** @var ConfigORM */
private $adminCFG; class AdminPlugin implements iPluginHeader
{
/**
* @var string private Config $config;
*/ private Router $router;
private $pluginPath; private Models $models;
private Views $views;
/** private Controllers $controllers;
* @inheritDoc private AuthenticationPlugin $authPlugin;
*/
public function init() private string $pluginKey = 'admin';
{ private string $pluginPath;
// Make a listener for a web request private string $adminURL;
Events::addListener([$this, 'routeWebRequestEventListener'], 'routeWebRequestEvent', Priority::NORMAL); private string $apiRoute;
$this->pluginPath = dirname(__DIR__, 3); private string $webRoute;
}
private ?Session $session = null;
public function routeWebRequestEventListener(RouteWebRequestEvent $event) private bool $loadAsync = false;
{
// If this request has nothing to do with the admin interface, don't bother routing the request /**
if (substr($event->uriString, 0, strlen($this->pluginKey)) !== $this->pluginKey) * @inheritDoc
return; * @throws EventException
* @throws FactoryException
Logger::log("Administration: observed admin request. Activating plugin."); * @throws ConfigException
*/
public function init()
// Load the dependencies {
$this->config = Factory::getInstance('config'); // Make a listener for a web request
$this->router = Factory::getInstance('router'); $this->pluginPath = dirname(__DIR__, 3);
$this->models = Factory::getInstance('models');
$this->views = Factory::getInstance('views'); // Load the admin configuration
$this->controllers = Factory::getInstance('controllers'); $this->config = Factory::getInstance('config');
$this->config->addComponentPath($this->pluginPath, Priority::LOWEST);
// Load the admin configuration $adminCFG = $this->config->getConfig('admin');
$this->config->addComponentPath($this->pluginPath, Priority::LOWEST);
$this->adminCFG = $this->config->getConfig('admin'); // If admin is not enabled, stop here
if (!$adminCFG->get('admin_enabled'))
// If admin is not enabled, stop here return;
if (!$this->adminCFG->get('admin_enabled'))
return; // Register the event
Events::addListener([$this, 'routeWebRequestEventListener'], 'routeWebRequestEvent', Priority::NORMAL);
// Now load the pluginKey
$this->pluginKey = $this->adminCFG->get('admin_url'); // Now load the pluginKey
$this->pluginKey = $adminCFG->get('admin_url');
// If it does, register everything
/** @var Resources $resources */ // Settle admin URL
$resources = Factory::getInstance('resources'); $webURL = $this->config->getConfig("web")->get("base_url");
$this->adminURL = $webURL . "/" . $this->pluginKey;
// Serve the AdminLTE distribution files
$adminLTE = $this->pluginPath . DS . 'vendor' . DS . 'almasaeed2010' . DS . 'adminlte'; // Determine routing paths
$resources->registerResources('admin/dist', $adminLTE . DS . 'dist'); $this->webRoute = '^' . $this->pluginKey . '(|\/(?P<viewName>.*?)(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?))))';
$resources->registerResources('admin/plugins', $adminLTE . DS . 'plugins'); $this->apiRoute = '^' . $this->pluginKey . '(|\/(?P<viewName>.*?)(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?)))).json';
// And serve the actual pages // Load the dependencies
$routeString = '^' . $this->pluginKey . '(|\/(?P<viewName>.*?)(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?))))'; $this->router = Factory::getInstance('router');
$this->router->addRoute($routeString, ['callable' => [$this, 'adminCallable']], Priority::HIGHEST); $this->models = Factory::getInstance('models');
$this->views = Factory::getInstance('views');
// And add componentPaths for models, views, controllers $this->controllers = Factory::getInstance('controllers');
$this->models->addComponentPath($this->pluginPath . DS . 'models', Priority::LOW); }
$this->views->addComponentPath($this->pluginPath . DS . 'views', Priority::LOW);
$this->controllers->addComponentPath($this->pluginPath . DS . 'controllers', Priority::LOW); /**
} * @param RouteWebRequestEvent $event
* @return void
public function adminCallable(array $matches, string $routeString) * @throws FactoryException
{ * @throws WebException
Logger::log("AdminCallable called. Loading admin page."); */
public function routeWebRequestEventListener(RouteWebRequestEvent $event): void
// Load layouts and assign componentPath, in case the loaded view needs access to it {
/** @var Layout $layouts */ Logger::log("Administration: observed web request. Activating plugin.");
$layouts = Factory::getInstance('layouts');
$layouts->addComponentPath($this->pluginPath . DS . 'layouts', Priority::LOW); // If it does, register everything
/** @var Resources $resources */
// And add a layoutLoadEventListener, to add global administration variables $resources = Factory::getInstance('resources');
Events::addListener([$this, 'layoutLoadEventListener'], 'layoutLoadEvent', Priority::NORMAL);
// Serve the AdminLTE distribution files
// Let's generate the sidebar $adminLTE = dirname(Core::$coreDir, 4) . DS . 'vendor' . DS . 'almasaeed2010' . DS . 'adminlte';
$finder = new PageFinder(); $resources->registerResources('admin/lte_dist', $adminLTE . DS . 'dist');
$sidebar = $finder->generateSidebar(); $resources->registerResources('admin/lte_plugins', $adminLTE . DS . 'plugins');
$resources->registerResources("admin/admin_dist", $this->pluginPath . DS . 'www' . DS . 'dist');
// Afterwards, pass on to the admin view and its contents
Logger::log("Forwarding request to Router::defaultCallable()."); // @todo TEMPORARY!!
$matches['viewType'] = 'admin'; //$resources->registerResources("lte_top", $adminLTE);
$content = $this->router->defaultCallable($matches, $routeString);
// And serve the actual pages
// Reset and assign $this->router->addRoute('^' . $this->pluginKey, ['viewName' => 'dashboard', 'callable' => [$this, 'adminCallable']], Priority::HIGHEST);
Logger::log("Generating panel wrapper."); $this->router->addRoute($this->apiRoute, ['callable' => [$this, 'adminCallable']], Priority::HIGH);
$layouts->reset(false); $this->router->addRoute($this->webRoute, ['callable' => [$this, 'adminCallable']], Priority::HIGH);
$layouts->assign('sidebar', $sidebar); }
$layouts->assign('content', $content);
/**
// And load the page * @param array $matches
Logger::log("Forwarding output back to Router."); * @param array $routeData
$page = $layouts->get('main/panel'); * @param string $routeString
return $page; * @return string|null
} * @throws EventException
* @throws FactoryException
/** * @throws LayoutException
* Listener for LayoutLoadEvent * @throws OutputException
* */
* Assigns variables to Layout that this plugin's layouts may require. public function adminCallable(array $matches, array $routeData, string $routeString): ?string
* {
* @param LayoutLoadEvent $event Logger::log("AdminCallable called. Loading admin page.");
*/
public function layoutLoadEventListener(LayoutLoadEvent $event) // Add componentPaths for models, views, controllers
{ $this->models->addComponentPath($this->pluginPath . DS . 'models' . DS . 'main', Priority::LOW);
$event->assign('adminKey', $this->pluginKey); $this->views->addComponentPath($this->pluginPath . DS . 'views' . DS . 'main', Priority::LOW);
} $this->controllers->addComponentPath($this->pluginPath . DS . 'controllers' . DS . 'main', Priority::LOW);
/** // Load layouts and assign componentPath, in case the loaded view needs access to it
* @inheritDoc /** @var Layout $layouts */
*/ $layouts = Factory::getInstance('layouts');
public function getName(): string $layouts->addComponentPath($this->pluginPath . DS . 'layouts', Priority::LOW);
{
return 'FuzeWorksAdministration'; // And add a layoutLoadEventListener, to add global administration variables
} Events::addListener([$this, 'layoutLoadEventListener'], 'layoutLoadEvent', Priority::HIGH);
Events::addListener([$this, 'verifyViewPermissions'], 'routerCallViewEvent', Priority::HIGH);
/** Events::addListener([$this, 'logViewNameAndIcon'], 'routerCallViewEvent', Priority::NORMAL);
* @inheritDoc
*/ if (class_exists("\FuzeWorks\Async\Tasks"))
public function getClassesPrefix(): ?string {
{ // Mark async to be loaded
return null; $this->loadAsync = true;
}
// Add componentPaths for models, views, controllers
/** $this->models->addComponentPath($this->pluginPath . DS . 'models' . DS . 'async', Priority::LOW);
* @inheritDoc $this->views->addComponentPath($this->pluginPath . DS . 'views' . DS . 'async', Priority::LOW);
*/ $this->controllers->addComponentPath($this->pluginPath . DS . 'controllers' . DS . 'async', Priority::LOW);
public function getSourceDirectory(): ?string }
{
return null; // Load the current session
} /** @var Output $output */
$this->authPlugin = Factory::getInstance("plugins")->get('auth');
/** $output = Factory::getInstance("output");
* @inheritDoc
*/ // Redirect the user to login if they're not logged in
public function getPluginClass(): ?string $this->session = $this->authPlugin->sessions->start();
{ if ($this->session->user->id === "0")
return null; {
} $output->setHeader("Location: " . $this->authPlugin->getAuthenticationURL() . "/login?location=" . $this->getAdminURL());
return "";
}
// Let's generate the sidebar
$finder = new PageFinder();
$sidebar = $finder->generateSidebar($this->session);
$footer = "";
// Afterwards, pass on to the admin view and its contents
Logger::log("Forwarding request to Router::defaultCallable().");
// Determine viewType
if ($routeString === $this->apiRoute)
$routeData['viewType'] = 'AdminAPI';
else
$routeData['viewType'] = 'admin';
try {
// Fetch content from requested view
$content = $this->router->defaultCallable($matches, $routeData, $routeString);
// If content is false, nothing is found and should return 404
if (is_bool($content) && $content === false)
{
$output->setStatusHeader(404);
$content = $layouts->get("main/error404");
$this->selectedMethodName = "Not found";
}
// And check for footer code
if (!empty($this->footerMethod))
{
if (!method_exists($this->selectedView, $this->footerMethod))
throw new Exception("Could not load view. Requested footerMethod not found.");
$footer = call_user_func_array([$this->selectedView, $this->footerMethod], []);
}
} catch (HaltException $e) {
$output->setStatusHeader(403);
$content = $layouts->get("main/error403");
$this->selectedMethodName = "Unauthorized";
} catch (Exception|\Throwable $e) {
Logger::exceptionHandler($e, false);
$output->setStatusHeader(500);
$content = $layouts->get("main/error500");
$this->selectedMethodName = "Fatal error";
}
// If an api is requested, return the content as a json object
if ($routeData['viewType'] === 'AdminAPI')
{
$output->setContentType('json');
return json_encode($content);
}
// Otherwise, load the panel and return that
Logger::log("Generating panel wrapper.");
$layouts->reset(false);
$layouts->assign("pageTitle", $this->selectedMethodName);
$layouts->assign("pageIcon", $this->selectedMethodIcon);
$layouts->assign('sidebar', $sidebar);
$layouts->assign('content', $content);
$layouts->assign("footer", $footer);
return $layouts->get('main/panel');
}
public function getAdminURL(): string
{
return $this->adminURL;
}
/**
* Listener for LayoutLoadEvent
*
* Assigns variables to Layout that this plugin's layouts may require.
*
* @param LayoutLoadEvent $event
*/
public function layoutLoadEventListener(LayoutLoadEvent $event)
{
$event->assign('adminKey', $this->pluginKey);
$event->assign('session', $this->session);
$event->assign('authURL', $this->authPlugin->getAuthenticationURL());
$event->assign("loadAsync", $this->loadAsync);
}
/**
* Listener for RouterCallViewEvent
*
* Verifies that the current user has access to the requested methods.
*
* @param RouterCallViewEvent $event
* @return RouterCallViewEvent
* @throws ReflectionException
* @throws HaltException
*/
public function verifyViewPermissions(RouterCallViewEvent $event): RouterCallViewEvent
{
// Load reflector
$reflector = new ReflectionClass(get_class($event->view));
// Pass over each requested method
for ($i = Priority::getHighestPriority(); $i <= Priority::getLowestPriority(); $i++) {
if (!isset($event->viewMethods[$i]))
continue;
foreach ($event->viewMethods[$i] as $key => $method)
{
// If the method doesn't exist, skip it in general
if (!method_exists($event->view, $method))
{
unset($event->viewMethods[$i][$key]);
continue;
}
// If the method exists, verify permission
$methodReflector = $reflector->getMethod($method);
$permissionAttributes = $methodReflector->getAttributes(PermissionAttribute::class);
if (!empty($permissionAttributes))
{
// Fetch nodes
$nodes = $permissionAttributes[0]->newInstance()->getValue();
// Check if any of the nodes are permitted
$found = false;
foreach ($nodes as $node)
if ($this->session->hasPermission($node))
$found = true;
// If not, skip
if (!$found)
{
Logger::logWarning("Current user does not have permission for the requested method. Blocking.");
$event->setCancelled(true);
}
}
}
}
return $event;
}
protected string $selectedMethodName = "";
protected string $selectedMethodIcon = "";
protected string $footerMethod = "";
protected View $selectedView;
public function logViewNameAndIcon(RouterCallViewEvent $event): RouterCallViewEvent
{
$reflector = new ReflectionClass(get_class($event->view));
$this->selectedView = $event->view;
for ($i = Priority::getHighestPriority(); $i <= Priority::getLowestPriority(); $i++)
{
if (!isset($event->viewMethods[$i]))
continue;
foreach ($event->viewMethods[$i] as $key => $method)
{
$methodReflector = $reflector->getMethod($method);
// Check for display attribute.
$displayAttributes = $methodReflector->getAttributes(DisplayAttribute::class);
$iconAttributes = $methodReflector->getAttributes(IconAttribute::class);
$footerAttributes = $methodReflector->getAttributes(FooterCodeMethodAttribute::class);
$this->selectedMethodName = !empty($displayAttributes) ? $displayAttributes[0]->newInstance()->getValue() : ucfirst($methodReflector->getName());
$this->selectedMethodIcon = !empty($iconAttributes) ? $iconAttributes[0]->newInstance()->getValue() : "";
$this->footerMethod = !empty($footerAttributes) ? $footerAttributes[0]->newInstance()->getvalue() : "";
}
}
return $event;
}
/**
* @inheritDoc
*/
public function getName(): string
{
return 'admin';
}
/**
* @inheritDoc
*/
public function getClassesPrefix(): ?string
{
return null;
}
/**
* @inheritDoc
*/
public function getSourceDirectory(): ?string
{
return null;
}
/**
* @inheritDoc
*/
public function getPluginClass(): ?string
{
return null;
}
} }

129
src/FuzeWorks/Administration/AdminView.php Normal file → Executable file
View File

@ -1,61 +1,70 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
namespace FuzeWorks\Administration; namespace FuzeWorks\Administration;
use FuzeWorks\Factory; use FuzeWorks\Authentication\AuthenticationPlugin;
use FuzeWorks\Layout; use FuzeWorks\Authentication\Model\Session;
use FuzeWorks\Priority; use FuzeWorks\Authentication\Model\Users;
use FuzeWorks\WebView; use FuzeWorks\Factory;
use FuzeWorks\Layout;
abstract class AdminView extends WebView use FuzeWorks\Priority;
{ use FuzeWorks\WebView;
/** @var Layout */ abstract class AdminView extends WebView
protected $layouts; {
protected Layout $layouts;
/** protected Session $session;
* The priority of this AdminView. protected Users $users;
*
* @var int /**
*/ * The priority of this AdminView.
protected $priority = Priority::NORMAL; */
protected int $priority = Priority::NORMAL;
public function __construct()
{ public function __construct()
parent::__construct(); {
parent::__construct();
$this->layouts = Factory::getInstance('layouts');
} // Set layouts
$this->layouts = Factory::getInstance('layouts');
/** @var AuthenticationPlugin $plugin */
$plugin = $this->plugins->get('auth');
// Set current session
$this->users = $plugin->users;
$this->session = $plugin->sessions->getCurrentSession();
}
} }

View File

@ -0,0 +1,41 @@
<?php
/**
* FuzeWorksAdministration
*
* Copyright (C) 2013-2021 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2021, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace FuzeWorks\Administration\Attributes;
interface AdminAttribute
{
public function getValue();
}

View File

@ -0,0 +1,51 @@
<?php
/**
* FuzeWorksAdministration
*
* Copyright (C) 2013-2023 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2023, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace FuzeWorks\Administration\Attributes;
use Attribute;
#[Attribute]
class DashboardAttribute implements AdminAttribute
{
public function __construct()
{
}
public function getValue()
{
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* FuzeWorksAdministration
*
* Copyright (C) 2013-2021 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2021, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace FuzeWorks\Administration\Attributes;
use Attribute;
#[Attribute]
class DisplayAttribute implements AdminAttribute
{
const VALUE = '';
private string $value;
public function __construct(string $value = '')
{
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* FuzeWorksAdministration
*
* Copyright (C) 2013-2021 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2021, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace FuzeWorks\Administration\Attributes;
use Attribute;
#[Attribute]
class FooterCodeMethodAttribute implements AdminAttribute
{
const VALUE = '';
private string $value;
public function __construct(string $value = '')
{
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* FuzeWorksAdministration
*
* Copyright (C) 2013-2021 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2021, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace FuzeWorks\Administration\Attributes;
use Attribute;
#[Attribute]
class HiddenAttribute implements AdminAttribute
{
public function getValue(): bool
{
return true;
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* FuzeWorksAdministration
*
* Copyright (C) 2013-2021 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2021, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace FuzeWorks\Administration\Attributes;
use Attribute;
#[Attribute]
class IconAttribute implements AdminAttribute
{
private string $value;
public function __construct(string $value = '')
{
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* FuzeWorksWorkspace.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2022 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2022, i15. (http://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link https://i15.nl
* @since Version 2.0.0
*
* @version Version 2.0.0
*/
namespace FuzeWorks\Administration\Attributes;
use Attribute;
#[Attribute]
class PermissionAttribute implements AdminAttribute
{
private array $nodes;
public function __construct(array $nodes = [])
{
$this->nodes = $nodes;
}
public function getValue(): array
{
return $this->nodes;
}
}

View File

@ -0,0 +1,54 @@
<?php
/**
* FuzeWorksAdministration
*
* Copyright (C) 2013-2021 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2021, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace FuzeWorks\Administration\Attributes;
use Attribute;
use FuzeWorks\Priority;
#[Attribute]
class PriorityAttribute implements AdminAttribute
{
private int $priority;
public function __construct(int $value = Priority::NORMAL)
{
$this->priority = $value;
}
public function getValue(): int
{
return $this->priority;
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* FuzeWorksWorkspace.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2022 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2022, i15. (http://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link https://i15.nl
* @since Version 2.0.0
*
* @version Version 2.0.0
*/
namespace FuzeWorks\Administration\Exceptions;
use FuzeWorks\Exception\PluginException;
class AdminPluginException extends PluginException
{
}

View File

@ -0,0 +1,42 @@
<?php
/**
* FuzeWorksWorkspace.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2022 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2022, i15. (http://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link https://i15.nl
* @since Version 2.0.0
*
* @version Version 2.0.0
*/
namespace FuzeWorks\Administration\Exceptions;
class UnauthorizedException extends AdminPluginException
{
}

514
src/FuzeWorks/Administration/PageFinder.php Normal file → Executable file
View File

@ -1,230 +1,286 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
namespace FuzeWorks\Administration; namespace FuzeWorks\Administration;
use FuzeWorks\Administration\Events\AdminGenerateSidebarEvent; use FuzeWorks\Administration\Attributes\DisplayAttribute;
use FuzeWorks\Events; use FuzeWorks\Administration\Attributes\HiddenAttribute;
use FuzeWorks\Factory; use FuzeWorks\Administration\Attributes\IconAttribute;
use FuzeWorks\Logger; use FuzeWorks\Administration\Attributes\PermissionAttribute;
use FuzeWorks\Priority; use FuzeWorks\Administration\Attributes\PriorityAttribute;
use FuzeWorks\Views; use FuzeWorks\Administration\Events\AdminFindViewsEvent;
use phpDocumentor\Reflection\DocBlockFactory; use FuzeWorks\Administration\Events\AdminGenerateSidebarEvent;
use ReflectionClass; use FuzeWorks\Authentication\Model\Session;
use ReflectionException; use FuzeWorks\Events;
use ReflectionMethod; use FuzeWorks\Factory;
use FuzeWorks\Logger;
class PageFinder use FuzeWorks\Priority;
{ use FuzeWorks\Views;
use ReflectionClass;
use ReflectionException;
/** use ReflectionMethod;
* Generates an array of all sidebar elements.
* class PageFinder
* First searches for all the available AdminViews. Then parses over all their methods and populates the array {
* using each method's individual docblock.
*
* @return array protected Session $session;
*/
public function generateSidebar(): array /**
{ * Generates an array of all sidebar elements.
Logger::log("Generating sidebar..."); *
$event = Events::fireEvent('adminGenerateSidebarEvent'); * First searches for all the available AdminViews. Then parses over all their methods and populates the array
if ($event->isCancelled()) * using each method's attributes.
return []; *
* @return array
// First find all views */
$viewFileList = $this->findViews(); public function generateSidebar(Session $session): array
Logger::log("Found " . count($viewFileList) . ' admin views.'); {
$this->session = $session;
// Then sort all those views into methods in the sidebar
$sorted = $this->sort($viewFileList); Logger::log("Generating sidebar...");
$event = Events::fireEvent('adminGenerateSidebarEvent');
// Log the result and return if ($event->isCancelled())
Logger::log("Found " . count($sorted) . ' entries for sidebar.'); return [];
return $sorted;
} // First find all views
$views = $this->findViews();
/** Logger::log("Found " . count($views) . ' admin views.');
* Parse over every view component path and find all admin views.
* // Then sort all those views into methods in the sidebar
* Return a file list. $sorted = $this->sortSidebar($views);
*
* @return array // Log the result and return
*/ Logger::log("Found " . count($sorted) . ' entries for sidebar.');
protected function findViews(): array return $sorted;
{ }
/** @var Views $views */
$views = Factory::getInstance('views'); /**
* Find all views that have methods with attributes of a specific kind.
// First collect all the known paths *
$paths = []; * @param string $attributeClass
for ($i = Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) * @return string[]
$paths[$i] = $views->getComponentPaths($i); */
public function findViewsWithAttribute(string $attributeClass): array
// Then go over every individual paths {
$viewFiles = []; // First find all admin views
foreach ($paths as $priority => $foldersArray) $views = $this->findViews();
{ return $this->sortByAttribute($views, $attributeClass);
foreach ($foldersArray as $folder) }
{
// Get the contents from the folder /**
$contents = array_diff(scandir($folder), array('..', '.')); * Parse over every view component path and find all admin views.
*
// Then go over the folders, and see which ones are an admin view * Return a file list.
foreach ($contents as $file) *
{ * @return string[]
// If the file matches the expected filename, add it to the list */
if (substr($file, 0, 11) === 'view.admin.' && !isset($viewFiles[$file])) protected function findViews(): array
$viewFiles[$file] = $folder . DS . $file; {
} /** @var Views $views */
} $views = Factory::getInstance('views');
}
// First collect all the known paths
return $viewFiles; $paths = [];
} for ($i = Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++)
$paths[$i] = $views->getComponentPaths($i);
protected function sort(array $viewFileList): array
{ // Then go over every individual paths
// Reflection docblock factory $views = [];
$factory = DocBlockFactory::createInstance(); foreach ($paths as $priority => $foldersArray)
{
// Start sorting all methods foreach ($foldersArray as $folder)
$entries = []; {
foreach ($viewFileList as $viewId => $viewFile) // Get the contents from the folder
{ $contents = array_diff(scandir($folder), array('..', '.'));
// Load the file
require_once $viewFile; // Then go over the folders, and see which ones are an admin view
foreach ($contents as $file)
// Determine the className {
$id = substr($viewId, 11, -4); // If the file matches the expected filename, add it to the list
$className = 'Application\View\\' . ucfirst($id) . 'AdminView'; if (!str_starts_with($file, 'view.admin.'))
continue;
// Try and reflect on this class.
try { // Attempt to load the view
$reflector = new ReflectionClass($className); $viewFile = $folder . DS . $file;
} catch (ReflectionException $e) { $id = substr($file, 11, -4);
// If that doesn't work, simply ignore it. Don't drag the whole interface down. $className = 'Application\View\\' . ucfirst($id) . 'AdminView';
continue; require_once($viewFile);
} if (!class_exists($className))
continue;
// Check if this class actually inherits AdminView
$parent = $reflector->getParentClass(); $views[] = $className;
if ($parent === false || $parent->getName() !== 'FuzeWorks\Administration\AdminView') }
continue; }
}
// Select all methods
$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC); return $views;
}
// And pass over all of them
foreach ($methods as $method) { /**
// Prepare the entry output * Returns all views which have methods which have Attributes with the attributeClassName
$entry = [ *
'url' => ($id !== 'index' ? $id : '') . ($method->getName() !== 'index' ? '/' . $method->getName() : ''), * @param string[] $views
'priority' => Priority::NORMAL, * @param string $attributeClassName
'display' => $reflector->getShortName() . '/' . $method->getName(), * @return string[]
'icon' => 'round' */
]; protected function sortByAttribute(array $views, string $attributeClassName): array
{
// Only allow the methods in the actual class. Not from the parent method. $out = [];
if ($method->class !== $className) foreach ($views as $view)
continue; {
try {
// Then fetch the docComments. $reflector = new ReflectionClass($view);
$docComment = $method->getDocComment(); } catch (ReflectionException $e) {
if ($docComment !== false) // If reflector doesn't work, simply ignore it.
{ continue;
$docBlock = $factory->create($docComment); }
$docTags = $docBlock->getTags();
// Check if the class actually inherits AdminView
// Find known tags if (!$reflector->isSubclassOf(AdminView::class))
$hidden = $docBlock->getTagsByName('hidden'); continue;
$display = $docBlock->getTagsByName('display');
$icon = $docBlock->getTagsByName('icon'); // Select all methods
$priority = $docBlock->getTagsByName('priority'); $methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method)
// First test for hidden. If found, it should not be added to the sidebar if (!empty($method->getAttributes($attributeClassName)))
if (!empty($hidden)) $out[] = $view;
continue; }
// Then test for display return $out;
if (!empty($display)) }
$entry['display'] = (string) $display[0];
protected function sortSidebar(array $views): array
// Then test for icon {
if (!empty($icon)) // Start sorting all methods
$entry['icon'] = (string) $icon[0]; $entries = [];
foreach ($views as $view)
// Then test for priority {
if (!empty($priority)) // Try and reflect on this class.
{ try {
$priority = (string) $priority[0]; $reflector = new ReflectionClass($view);
switch ($priority) { } catch (ReflectionException $e) {
case 'Priority::LOWEST': // If that doesn't work, simply ignore it. Don't drag the whole interface down.
$entry['priority'] = 5; continue;
break; }
case 'Priority::LOW':
$entry['priority'] = 4; // Check if this class actually inherits AdminView
break; if (!$reflector->isSubclassOf("FuzeWorks\Administration\AdminView"))
case 'Priority::NORMAL': continue;
$entry['priority'] = 3;
break; // Determine view Id
case 'Priority::HIGH': $id = strtolower(substr($reflector->getShortName(), 0, -9));
$entry['priority'] = 2;
break; // Select all methods
case 'Priority::HIGHEST': $methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);
$entry['priority'] = 1;
break; // And pass over all of them
case 'Priority::MONITOR': foreach ($methods as $method) {
$entry['priority'] = 0; // Prepare the entry output
break; $entry = [
default: 'url' => ($id !== 'index' ? $id : '') . ($method->getName() !== 'index' ? '/' . $method->getName() : ''),
Logger::logError("Method " . $reflector->getName() . '::' . $method->getName() . ' has invalid value for @priority.'); 'priority' => Priority::NORMAL,
$entry['priority'] = 3; 'display' => $reflector->getShortName() . '::' . $method->getName(),
break; 'icon' => 'round'
} ];
}
} // Only allow the methods in the actual class. Not from the parent method.
if ($method->class !== $view)
// Write to entries continue;
$entries[] = $entry;
} // Read attributes
} // First read hidden attribute. If present, skip this method
$hiddenAttributes = $method->getAttributes(HiddenAttribute::class);
// And finally return those if (!empty($hiddenAttributes))
return $entries; continue;
}
// Then check for permissions
$permissionAttributes = $method->getAttributes(PermissionAttribute::class);
if (!empty($permissionAttributes))
{
// Fetch nodes
$nodes = $permissionAttributes[0]->newInstance()->getValue();
// Check if any of the nodes are permitted
$found = false;
foreach ($nodes as $node)
if ($this->session->hasPermission($node))
$found = true;
// If not, skip
if (!$found)
continue;
}
// Then check for display attribute.
$displayAttributes = $method->getAttributes(DisplayAttribute::class);
if (!empty($displayAttributes))
$entry['display'] = $displayAttributes[0]->newInstance()->getValue();
// Then check for icon
$iconAttributes = $method->getAttributes(IconAttribute::class);
if (!empty($iconAttributes))
$entry['icon'] = $iconAttributes[0]->newInstance()->getValue();
// Then check for priority
$priorityAttributes = $method->getAttributes(PriorityAttribute::class);
if (!empty($priorityAttributes))
$entry['priority'] = $priorityAttributes[0]->newInstance()->getValue();
// Add priority
if (!isset($entries[$entry['priority']]))
$entries[$entry['priority']] = [];
// Write to entries
$entries[$entry['priority']][] = $entry;
}
}
// Sort by priority
$out = [];
for ($i = Priority::getHighestPriority(); $i <= Priority::getLowestPriority(); $i++) {
if (!isset($entries[$i]))
continue;
$p = $entries[$i];
foreach ($p as $entry)
$out[] = $entry;
}
// And finally return the sidebar entries
return $out;
}
} }

124
test/bootstrap.php Normal file → Executable file
View File

@ -1,63 +1,63 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
require_once(dirname(__DIR__) . '/vendor/autoload.php'); require_once(dirname(__DIR__) . '/vendor/autoload.php');
$configurator = new \FuzeWorks\Configurator(); $configurator = new \FuzeWorks\Configurator();
// Set directories // Set directories
$configurator->setTempDirectory(__DIR__ . '/temp'); $configurator->setTempDirectory(__DIR__ . '/temp');
$configurator->setLogDirectory(__DIR__ . '/temp'); $configurator->setLogDirectory(__DIR__ . '/temp');
// Other values // Other values
$configurator->setTimeZone('Europe/Amsterdam'); $configurator->setTimeZone('Europe/Amsterdam');
// Add components and plugins // Add components and plugins
$webComponent = new \FuzeWorks\WebComponent(); $webComponent = new \FuzeWorks\WebComponent();
$webComponent->enableComponent(); $webComponent->enableComponent();
$configurator->addComponent($webComponent); $configurator->addComponent($webComponent);
// Add LayoutComponent // Add LayoutComponent
$configurator->addComponent(new \FuzeWorks\LayoutComponent()); $configurator->addComponent(new \FuzeWorks\LayoutComponent());
// Add TracyComponent // Add TracyComponent
$configurator->addComponent(new \FuzeWorks\TracyComponent()); $configurator->addComponent(new \FuzeWorks\TracyComponent());
// Debug related // Debug related
$configurator->enableDebugMode(); $configurator->enableDebugMode();
$configurator->setDebugAddress('ALL'); $configurator->setDebugAddress('ALL');
$container = $configurator->createContainer(); $container = $configurator->createContainer();
$container->plugins->addPlugin(new \FuzeWorks\Administration\AdminPlugin()); $container->plugins->addPlugin(new \FuzeWorks\Administration\AdminPlugin());
return $container; return $container;

View File

@ -0,0 +1,76 @@
<?php
/**
* i15Site.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2022 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2022, i15. (http://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link https://i15.nl
* @since Version 2.0.0
*
* @version Version 2.0.0
*/
namespace Application\View;
use Application\Controller\AsyncController;
use FuzeWorks\Administration\AdminView;
use FuzeWorks\Administration\Attributes\DisplayAttribute;
use FuzeWorks\Administration\Attributes\HiddenAttribute;
use FuzeWorks\Async\Tasks;
use FuzeWorks\Controller;
class AsyncAdminView extends AdminView
{
/**
* @var AsyncController
*/
protected Controller $controller;
protected Tasks $tasks;
#[HiddenAttribute]
public function __construct()
{
parent::__construct();
/** @var Tasks $lib */
$lib = $this->libraries->get("tasks");
$this->tasks = $lib;
}
#[HiddenAttribute]
#[DisplayAttribute("Overview")]
public function index()
{
// Read out all tasks
$storage = $this->tasks->getTaskStorage();
$tasks = $storage->readTasks();
// And assign them
$this->layouts->assign("tasks", $tasks);
return json_encode($tasks);
}
}

View File

@ -0,0 +1,67 @@
<?php
/**
* i15Site.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2022 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2022, i15. (http://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link https://i15.nl
* @since Version 2.0.0
*
* @version Version 2.0.0
*/
namespace Application\View;
use Application\Controller\AsyncController;
use FuzeWorks\Administration\AdminView;
use FuzeWorks\Async\Tasks;
use FuzeWorks\Controller;
class AsyncAdminApiView extends AdminView
{
/**
* @var AsyncController
*/
protected Controller $controller;
protected Tasks $tasks;
public function __construct()
{
parent::__construct();
/** @var Tasks $lib */
$lib = $this->libraries->get("tasks");
$this->tasks = $lib;
}
public function index()
{
return ["Hello world"];
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Application\View;
use Application\Controller\CacheController;
use FuzeWorks\Administration\AdminView;
use FuzeWorks\Administration\Attributes\DisplayAttribute;
use FuzeWorks\Administration\Attributes\HiddenAttribute;
use FuzeWorks\Administration\Attributes\IconAttribute;
use FuzeWorks\Administration\Attributes\PermissionAttribute;
use FuzeWorks\Administration\Attributes\PriorityAttribute;
use FuzeWorks\Controller;
use FuzeWorks\Factory;
use FuzeWorks\ObjectStorage\ObjectStorageComponent;
use FuzeWorks\Priority;
use Psr\SimpleCache\CacheInterface;
class CacheAdminView extends AdminView
{
/** @var CacheController $controller */
protected Controller $controller;
#[DisplayAttribute("ObjectStorage"), IconAttribute("server"), PriorityAttribute(Priority::LOW)]
#[HiddenAttribute]
#[PermissionAttribute(["ADMIN"])]
public function index()
{
$items = $this->controller->getCacheItems();
return json_encode($items);
}
}

View File

@ -1,78 +1,68 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
namespace Application\View; namespace Application\View;
use Application\Controller\SettingsController; use Application\Controller\DashboardController;
use FuzeWorks\Administration\AdminView; use FuzeWorks\Administration\AdminView;
use FuzeWorks\Administration\Attributes\DashboardAttribute;
class SettingsAdminView extends AdminView use FuzeWorks\Administration\Attributes\DisplayAttribute;
{ use FuzeWorks\Administration\Attributes\HiddenAttribute;
use FuzeWorks\Administration\Attributes\IconAttribute;
/** use FuzeWorks\Administration\Attributes\PriorityAttribute;
* @var SettingsController use FuzeWorks\Controller;
*/ use FuzeWorks\Priority;
protected $controller;
class DashboardAdminView extends AdminView
/** {
* Displays a list of settings files
* /**
* @display Settings * @var DashboardController
* @icon cog */
* @priority Priority::LOW protected Controller $controller;
*/
public function index() #[DisplayAttribute("Dashboard"), IconAttribute("tachometer-alt"), PriorityAttribute(Priority::HIGHEST)]
{ public function index()
$files = $this->controller->findSettingsFiles(); {
$this->layouts->assign('files', $files); //$content = $this->controller->populateDashboard();
return $this->layouts->get('components/settings/overview'); //$this->layouts->assign('content', $content);
} }
/** #[HiddenAttribute, DashboardAttribute("Hello!")]
* @hidden public function test()
*/ {
public function view()
{ }
}
/**
* @hidden
*/
public function modify()
{
}
} }

View File

@ -0,0 +1,124 @@
<?php
/**
* FuzeWorks Framework Administration Plugin.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2020 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.3.0
*
* @version Version 1.3.0
*/
namespace Application\View;
use Application\Controller\SettingsController;
use FuzeWorks\Administration\AdminView;
use FuzeWorks\Administration\Attributes\DisplayAttribute;
use FuzeWorks\Administration\Attributes\HiddenAttribute;
use FuzeWorks\Administration\Attributes\IconAttribute;
use FuzeWorks\Administration\Attributes\PermissionAttribute;
use FuzeWorks\Administration\Attributes\PriorityAttribute;
use FuzeWorks\Controller;
use FuzeWorks\Exception\NotFoundException;
use FuzeWorks\Forms\Fields\CheckboxField;
use FuzeWorks\Forms\Fields\TextField;
use FuzeWorks\Forms\Forms;
use FuzeWorks\Priority;
class SettingsAdminView extends AdminView
{
/**
* @var SettingsController
*/
protected Controller $controller;
/**
* Displays a list of settings files
*/
#[DisplayAttribute("Config"), IconAttribute("cog"), PriorityAttribute(Priority::LOWEST)]
#[PermissionAttribute(["SUPER"])]
public function index()
{
$files = $this->controller->findSettingsFiles();
$this->layouts->assign('files', $files);
return $this->layouts->get('components/settings/overview');
}
/**
* @throws NotFoundException
*/
#[HiddenAttribute, DisplayAttribute("View config file")]
#[PermissionAttribute(["SUPER"])]
public function view(string $request)
{
// Determine parts
$parts = explode("/", $request);
$file = $parts[0];
$selector = intval($parts[1]);
// Select the file
$data = $this->controller->fetchFile($file, $selector);
// Create a form out of the data
/** @var Forms $forms */
$forms = $this->libraries->get("forms");
$form = $forms->getForm("ConfigEdit");
foreach ($data['data'] as $key => $value)
{
switch (gettype($value))
{
case "string":
case "integer":
$field = new TextField($key);
$field->setLabel($key)->setValue($value)->lock();
$form->field($field);
break;
case "boolean":
$field = new CheckboxField($key);
$field->setLabel($key)->setValue($value)->lock();
$form->field($field);
break;
default:
dump(gettype($value));
break;
}
}
// Assign and load layout
$this->layouts->assign('config', $data);
$this->layouts->assign('form', $form);
return $this->layouts->get('components/settings/view');
}
#[HiddenAttribute, DisplayAttribute("Edit config file")]
#[PermissionAttribute(["SUPER"])]
public function modify()
{
}
}

6
www/.htaccess Normal file → Executable file
View File

@ -1,4 +1,4 @@
RewriteEngine On RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?path=$1 [QSA,L] RewriteRule (.*) index.php?path=$1 [QSA,L]

29
www/dist/async.ts vendored Normal file
View File

@ -0,0 +1,29 @@
class AsyncManager {
protected readonly url: string = "/admin/async";
constructor(url?: string) {
if (url !== undefined)
this.url = url;
}
public getTaskList(onSuccess: Function, onError?: Function): void {
let xhr = new XMLHttpRequest();
xhr.open("GET", this.url + ".json", true);
xhr.send();
// @todo Continue
}
}
let async: AsyncManager = new AsyncManager();
const url = "/admin/async";
let xhr = new XMLHttpRequest();
xhr.open("GET", url + ".json", true);
xhr.send();
xhr.onload = function () {
if (xhr.status === 200) {
}
}

80
www/index.php Normal file → Executable file
View File

@ -1,41 +1,41 @@
<?php <?php
/** /**
* FuzeWorks Framework Administration Plugin. * FuzeWorks Framework Administration Plugin.
* *
* The FuzeWorks PHP FrameWork * The FuzeWorks PHP FrameWork
* *
* Copyright (C) 2013-2020 i15 * Copyright (C) 2013-2020 i15
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* @author i15 * @author i15
* @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl) * @copyright Copyright (c) 2013 - 2020, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License * @license https://opensource.org/licenses/MIT MIT License
* *
* @since Version 1.3.0 * @since Version 1.3.0
* *
* @version Version 1.3.0 * @version Version 1.3.0
*/ */
/** @var \FuzeWorks\Factory $container */ /** @var \FuzeWorks\Factory $container */
$container = require(dirname(__DIR__) . '/test/bootstrap.php'); $container = require(dirname(__DIR__) . '/test/bootstrap.php');
/** @var \FuzeWorks\WebComponent $web */ /** @var \FuzeWorks\WebComponent $web */
$web = $container->web; $web = $container->web;
$web->routeWebRequest(); $web->routeWebRequest();