Administration/src/FuzeWorks/Administration/PageFinder.php

286 lines
10 KiB
PHP
Executable File

<?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 FuzeWorks\Administration;
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\Administration\Events\AdminFindViewsEvent;
use FuzeWorks\Administration\Events\AdminGenerateSidebarEvent;
use FuzeWorks\Authentication\Model\Session;
use FuzeWorks\Events;
use FuzeWorks\Factory;
use FuzeWorks\Logger;
use FuzeWorks\Priority;
use FuzeWorks\Views;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
class PageFinder
{
protected Session $session;
/**
* Generates an array of all sidebar elements.
*
* First searches for all the available AdminViews. Then parses over all their methods and populates the array
* using each method's attributes.
*
* @return array
*/
public function generateSidebar(Session $session): array
{
$this->session = $session;
Logger::log("Generating sidebar...");
$event = Events::fireEvent('adminGenerateSidebarEvent');
if ($event->isCancelled())
return [];
// First find all views
$views = $this->findViews();
Logger::log("Found " . count($views) . ' admin views.');
// Then sort all those views into methods in the sidebar
$sorted = $this->sortSidebar($views);
// Log the result and return
Logger::log("Found " . count($sorted) . ' entries for sidebar.');
return $sorted;
}
/**
* Find all views that have methods with attributes of a specific kind.
*
* @param string $attributeClass
* @return string[]
*/
public function findViewsWithAttribute(string $attributeClass): array
{
// First find all admin views
$views = $this->findViews();
return $this->sortByAttribute($views, $attributeClass);
}
/**
* Parse over every view component path and find all admin views.
*
* Return a file list.
*
* @return string[]
*/
protected function findViews(): array
{
/** @var Views $views */
$views = Factory::getInstance('views');
// First collect all the known paths
$paths = [];
for ($i = Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++)
$paths[$i] = $views->getComponentPaths($i);
// Then go over every individual paths
$views = [];
foreach ($paths as $priority => $foldersArray)
{
foreach ($foldersArray as $folder)
{
// Get the contents from the folder
$contents = array_diff(scandir($folder), array('..', '.'));
// Then go over the folders, and see which ones are an admin view
foreach ($contents as $file)
{
// If the file matches the expected filename, add it to the list
if (!str_starts_with($file, 'view.admin.'))
continue;
// Attempt to load the view
$viewFile = $folder . DS . $file;
$id = substr($file, 11, -4);
$className = 'Application\View\\' . ucfirst($id) . 'AdminView';
require_once($viewFile);
if (!class_exists($className))
continue;
$views[] = $className;
}
}
}
return $views;
}
/**
* Returns all views which have methods which have Attributes with the attributeClassName
*
* @param string[] $views
* @param string $attributeClassName
* @return string[]
*/
protected function sortByAttribute(array $views, string $attributeClassName): array
{
$out = [];
foreach ($views as $view)
{
try {
$reflector = new ReflectionClass($view);
} catch (ReflectionException $e) {
// If reflector doesn't work, simply ignore it.
continue;
}
// Check if the class actually inherits AdminView
if (!$reflector->isSubclassOf(AdminView::class))
continue;
// Select all methods
$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method)
if (!empty($method->getAttributes($attributeClassName)))
$out[] = $view;
}
return $out;
}
protected function sortSidebar(array $views): array
{
// Start sorting all methods
$entries = [];
foreach ($views as $view)
{
// Try and reflect on this class.
try {
$reflector = new ReflectionClass($view);
} catch (ReflectionException $e) {
// If that doesn't work, simply ignore it. Don't drag the whole interface down.
continue;
}
// Check if this class actually inherits AdminView
if (!$reflector->isSubclassOf("FuzeWorks\Administration\AdminView"))
continue;
// Determine view Id
$id = strtolower(substr($reflector->getShortName(), 0, -9));
// Select all methods
$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);
// And pass over all of them
foreach ($methods as $method) {
// Prepare the entry output
$entry = [
'url' => ($id !== 'index' ? $id : '') . ($method->getName() !== 'index' ? '/' . $method->getName() : ''),
'priority' => Priority::NORMAL,
'display' => $reflector->getShortName() . '::' . $method->getName(),
'icon' => 'round'
];
// Only allow the methods in the actual class. Not from the parent method.
if ($method->class !== $view)
continue;
// Read attributes
// First read hidden attribute. If present, skip this method
$hiddenAttributes = $method->getAttributes(HiddenAttribute::class);
if (!empty($hiddenAttributes))
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;
}
}