2021-09-09 11:05:00 +02:00
< ? php
namespace App\Controller\Component ;
use Cake\Controller\Component ;
use Cake\Core\Configure ;
2021-10-08 10:08:02 +02:00
use Cake\Core\App ;
2021-09-09 11:05:00 +02:00
use Cake\Utility\Inflector ;
2021-09-09 13:12:52 +02:00
use Cake\Utility\Hash ;
2021-10-08 10:08:02 +02:00
use Cake\Filesystem\Folder ;
2021-09-09 11:05:00 +02:00
use Cake\Routing\Router ;
2021-09-09 13:12:52 +02:00
use Cake\ORM\TableRegistry ;
2021-10-08 10:08:02 +02:00
use Exception ;
use SidemenuNavigation\Sidemenu ;
require_once ( APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . 'sidemenu.php' );
2021-09-09 11:05:00 +02:00
class NavigationComponent extends Component
{
private $user = null ;
public $breadcrumb = null ;
2021-09-10 15:58:41 +02:00
public $iconToTableMapping = [
'Individuals' => 'address-book' ,
'Organisations' => 'building' ,
'EncryptionKeys' => 'key' ,
'SharingGroups' => 'user-friends' ,
'Broods' => 'network-wired' ,
'Roles' => 'id-badge' ,
'Users' => 'users' ,
'Inbox' => 'inbox' ,
'Outbox' => 'inbox' ,
'MetaTemplates' => 'object-group' ,
'LocalTools' => 'tools' ,
'Instance' => 'server' ,
2021-09-17 18:30:32 +02:00
'Tags' => 'tags' ,
2021-09-10 15:58:41 +02:00
];
2021-09-09 11:05:00 +02:00
public function initialize ( array $config ) : void
{
$this -> request = $config [ 'request' ];
2021-09-10 11:55:54 +02:00
}
public function beforeFilter ( $event )
{
2021-09-09 11:05:00 +02:00
$this -> fullBreadcrumb = $this -> genBreadcrumb ();
$this -> breadcrumb = $this -> getBreadcrumb ();
}
public function getSideMenu () : array
{
2021-10-08 10:08:02 +02:00
$sidemenu = new Sidemenu ( $this -> iconToTableMapping , $this -> request );
return $sidemenu -> get ();
2021-09-09 11:05:00 +02:00
}
public function getBreadcrumb () : array
{
2021-09-09 13:12:52 +02:00
$controller = $this -> request -> getParam ( 'controller' );
$action = $this -> request -> getParam ( 'action' );
2021-10-08 10:08:02 +02:00
if ( empty ( $this -> fullBreadcrumb [ $controller ][ $action ])) {
2021-09-09 13:12:52 +02:00
return [[
'label' => $controller ,
'url' => Router :: url ([ 'controller' => $controller , 'action' => $action ]),
]]; // no breadcrumb defined for this endpoint
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
$currentRoute = $this -> fullBreadcrumb [ $controller ][ $action ];
$breadcrumbPath = $this -> getBreadcrumbPath ( $currentRoute );
return $breadcrumbPath ;
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
public function getBreadcrumbPath ( array $currentRoute ) : array
2021-09-09 11:05:00 +02:00
{
2021-10-08 10:08:02 +02:00
$path = [];
$visitedURL = [];
while ( empty ( $visitedURL [ $currentRoute [ 'url' ]])) {
$visitedURL [ $currentRoute [ 'url' ]] = true ;
$path [] = $currentRoute ;
2021-09-09 11:05:00 +02:00
if ( ! empty ( $currentRoute [ 'after' ])) {
2021-10-08 10:08:02 +02:00
if ( is_callable ( $currentRoute [ 'after' ])) {
$route = $currentRoute [ 'after' ]();
} else {
$route = $currentRoute [ 'after' ];
}
if ( empty ( $route )) {
continue ;
}
$currentRoute = $route ;
2021-09-09 11:05:00 +02:00
}
}
2021-10-08 10:08:02 +02:00
$path = array_reverse ( $path );
2021-09-09 11:05:00 +02:00
return $path ;
}
2021-10-08 10:08:02 +02:00
public function genBreadcrumb () : array
2021-09-09 11:05:00 +02:00
{
2021-10-08 10:08:02 +02:00
$request = $this -> request ;
$bcf = new BreadcrumbFactory ( $this -> iconToTableMapping );
$fullConfig = $this -> getFullConfig ( $bcf , $this -> request );
return $fullConfig ;
}
private function loadNavigationClasses ( $bcf , $request )
{
$navigationClasses = [];
$navigationDir = new Folder ( APP . DS . 'Controller' . DS . 'Component' . DS . 'Navigation' );
$navigationFiles = $navigationDir -> find ( '.*\.php' , true );
foreach ( $navigationFiles as $navigationFile ) {
if ( $navigationFile == 'base.php' || $navigationFile == 'sidemenu.php' ) {
continue ;
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
$navigationClassname = str_replace ( '.php' , '' , $navigationFile );
require_once ( APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . $navigationFile );
$reflection = new \ReflectionClass ( " BreadcrumbNavigation \\ { $navigationClassname } Navigation " );
$navigationClasses [ $navigationClassname ] = $reflection -> newInstance ( $bcf , $request );
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
return $navigationClasses ;
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
public function getFullConfig ( $bcf , $request )
2021-09-09 11:05:00 +02:00
{
2021-10-08 10:08:02 +02:00
$navigationClasses = $this -> loadNavigationClasses ( $bcf , $request );
$CRUDControllers = [
'Individuals' ,
'Organisations' ,
'EncryptionKeys' ,
'SharingGroups' ,
'Broods' ,
'Roles' ,
'Users' ,
'Tags' ,
'LocalTools' ,
];
foreach ( $CRUDControllers as $controller ) {
$bcf -> setDefaultCRUDForModel ( $controller );
}
foreach ( $navigationClasses as $className => $class ) {
$class -> addRoutes ();
}
foreach ( $navigationClasses as $className => $class ) {
$class -> addParents ();
}
foreach ( $navigationClasses as $className => $class ) {
$class -> addLinks ();
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
foreach ( $navigationClasses as $className => $class ) {
$class -> addActions ();
}
return $bcf -> getEndpoints ();
}
}
class BreadcrumbFactory
{
private $endpoints = [];
private $iconToTableMapping = [];
public function __construct ( $iconToTableMapping )
{
$this -> iconToTableMapping = $iconToTableMapping ;
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
public function defaultCRUD ( string $controller , string $action , array $overrides = []) : array
2021-09-09 13:12:52 +02:00
{
$table = TableRegistry :: getTableLocator () -> get ( $controller );
2021-10-08 10:08:02 +02:00
$item = [];
if ( $action === 'index' ) {
$item = $this -> genRouteConfig ( $controller , $action , [
'label' => __ ( '{0} index' , Inflector :: humanize ( $controller )),
'url' => " / { $controller } /index " ,
'icon' => $this -> iconToTableMapping [ $controller ]
]);
} else if ( $action === 'view' ) {
$item = $this -> genRouteConfig ( $controller , $action , [
'label' => __ ( 'View' ),
'icon' => 'eye' ,
'url' => " / { $controller } /view/ { { id}} " ,
'url_vars' => [ 'id' => 'id' ],
'textGetter' => ! empty ( $table -> getDisplayField ()) ? $table -> getDisplayField () : 'id' ,
]);
} else if ( $action === 'add' ) {
$item = $this -> genRouteConfig ( $controller , $action , [
'label' => __ ( '[new {0}]' , $controller ),
'icon' => 'plus' ,
'url' => " / { $controller } /add " ,
]);
} else if ( $action === 'edit' ) {
$item = $this -> genRouteConfig ( $controller , $action , [
'label' => __ ( 'Edit' ),
'icon' => 'edit' ,
'url' => " / { $controller } /edit/ { { id}} " ,
'url_vars' => [ 'id' => 'id' ],
'textGetter' => ! empty ( $table -> getDisplayField ()) ? $table -> getDisplayField () : 'id' ,
]);
} else if ( $action === 'delete' ) {
$item = $this -> genRouteConfig ( $controller , $action , [
'label' => __ ( 'Delete' ),
'icon' => 'trash' ,
'url' => " / { $controller } /delete/ { { id}} " ,
'url_vars' => [ 'id' => 'id' ],
'textGetter' => ! empty ( $table -> getDisplayField ()) ? $table -> getDisplayField () : 'id' ,
]);
}
$item [ 'route_path' ] = " { $controller } : { $action } " ;
$item = array_merge ( $item , $overrides );
return $item ;
2021-09-09 13:12:52 +02:00
}
2021-10-08 10:08:02 +02:00
public function genRouteConfig ( $controller , $action , $config = [])
2021-09-09 11:05:00 +02:00
{
2021-10-08 10:08:02 +02:00
$routeConfig = [
'controller' => $controller ,
'action' => $action ,
'route_path' => " { $controller } : { $action } " ,
2021-09-09 11:05:00 +02:00
];
2021-10-08 10:08:02 +02:00
$routeConfig = $this -> addIfNotEmpty ( $routeConfig , $config , 'url' );
$routeConfig = $this -> addIfNotEmpty ( $routeConfig , $config , 'url_vars' );
$routeConfig = $this -> addIfNotEmpty ( $routeConfig , $config , 'icon' );
$routeConfig = $this -> addIfNotEmpty ( $routeConfig , $config , 'label' );
$routeConfig = $this -> addIfNotEmpty ( $routeConfig , $config , 'textGetter' );
return $routeConfig ;
}
private function addIfNotEmpty ( $arr , $data , $key , $default = null )
{
if ( ! empty ( $data [ $key ])) {
$arr [ $key ] = $data [ $key ];
} else {
if ( ! is_null ( $default )) {
$arr [ $key ] = $default ;
}
}
return $arr ;
}
public function addRoute ( $controller , $action , $config = []) {
$this -> endpoints [ $controller ][ $action ] = $this -> genRouteConfig ( $controller , $action , $config );
}
public function setDefaultCRUDForModel ( $controller )
{
$this -> addRoute ( $controller , 'index' , $this -> defaultCRUD ( $controller , 'index' ));
$this -> addRoute ( $controller , 'view' , $this -> defaultCRUD ( $controller , 'view' ));
$this -> addRoute ( $controller , 'add' , $this -> defaultCRUD ( $controller , 'add' ));
$this -> addRoute ( $controller , 'edit' , $this -> defaultCRUD ( $controller , 'edit' ));
$this -> addRoute ( $controller , 'delete' , $this -> defaultCRUD ( $controller , 'delete' ));
$this -> addParent ( $controller , 'view' , $controller , 'index' );
$this -> addParent ( $controller , 'add' , $controller , 'index' );
$this -> addParent ( $controller , 'edit' , $controller , 'index' );
$this -> addParent ( $controller , 'delete' , $controller , 'index' );
$this -> addSelfLink ( $controller , 'view' );
$this -> addLink ( $controller , 'view' , $controller , 'edit' );
$this -> addLink ( $controller , 'edit' , $controller , 'view' );
$this -> addSelfLink ( $controller , 'edit' );
$this -> addAction ( $controller , 'view' , $controller , 'add' );
$this -> addAction ( $controller , 'view' , $controller , 'delete' );
$this -> addAction ( $controller , 'edit' , $controller , 'add' );
$this -> addAction ( $controller , 'edit' , $controller , 'delete' );
}
public function get ( $controller , $action )
{
if ( empty ( $this -> endpoints [ $controller ]) || empty ( $this -> endpoints [ $controller ][ $action ])) {
throw new \Exception ( sprintf ( " Tried to add a reference to %s:%s which does not exists " , $controller , $action ), 1 );
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
return $this -> endpoints [ $controller ][ $action ];
}
public function getEndpoints ()
{
return $this -> endpoints ;
}
public function addParent ( string $sourceController , string $sourceAction , string $targetController , string $targetAction , $overrides = [])
{
$routeSourceConfig = $this -> get ( $sourceController , $sourceAction );
$routeTargetConfig = $this -> get ( $targetController , $targetAction );
$overrides = $this -> execClosureIfNeeded ( $overrides , $routeSourceConfig );
if ( ! is_array ( $overrides )) {
throw new \Exception ( sprintf ( " Override closure for %s:%s -> %s:%s must return an array " , $sourceController , $sourceAction , $targetController , $targetAction ), 1 );
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
$routeTargetConfig = array_merge ( $routeTargetConfig , $overrides );
$parents = array_merge ( $routeSourceConfig [ 'after' ] ? ? [], $routeTargetConfig );
$this -> endpoints [ $sourceController ][ $sourceAction ][ 'after' ] = $parents ;
}
public function addSelfLink ( string $controller , string $action )
{
$this -> addLink ( $controller , $action , $controller , $action , [
'selfLink' => true
]);
}
public function addLink ( string $sourceController , string $sourceAction , string $targetController , string $targetAction , $overrides = [])
{
$routeSourceConfig = $this -> getRouteConfig ( $sourceController , $sourceAction , true );
$routeTargetConfig = $this -> getRouteConfig ( $targetController , $targetAction );
$overrides = $this -> execClosureIfNeeded ( $overrides , $routeSourceConfig );
if ( is_null ( $overrides )) {
// Overrides is null, the link should not be added
return ;
}
if ( ! is_array ( $overrides )) {
throw new \Exception ( sprintf ( " Override closure for %s:%s -> %s:%s must return an array " , $sourceController , $sourceAction , $targetController , $targetAction ), 1 );
}
$routeTargetConfig = array_merge ( $routeTargetConfig , $overrides );
$links = array_merge ( $routeSourceConfig [ 'links' ] ? ? [], [ $routeTargetConfig ]);
$this -> endpoints [ $sourceController ][ $sourceAction ][ 'links' ] = $links ;
}
public function addAction ( string $sourceController , string $sourceAction , string $targetController , string $targetAction , $overrides = [])
{
$routeSourceConfig = $this -> getRouteConfig ( $sourceController , $sourceAction , true );
$routeTargetConfig = $this -> getRouteConfig ( $targetController , $targetAction );
$overrides = $this -> execClosureIfNeeded ( $overrides , $routeSourceConfig );
if ( ! is_array ( $overrides )) {
throw new \Exception ( sprintf ( " Override closure for %s:%s -> %s:%s must return an array " , $sourceController , $sourceAction , $targetController , $targetAction ), 1 );
}
$routeTargetConfig = array_merge ( $routeTargetConfig , $overrides );
$links = array_merge ( $routeSourceConfig [ 'actions' ] ? ? [], [ $routeTargetConfig ]);
$this -> endpoints [ $sourceController ][ $sourceAction ][ 'actions' ] = $links ;
}
public function removeLink ( string $sourceController , string $sourceAction , string $targetController , string $targetAction )
{
$routeSourceConfig = $this -> getRouteConfig ( $sourceController , $sourceAction , true );
if ( ! empty ( $routeSourceConfig [ 'links' ])) {
foreach ( $routeSourceConfig [ 'links' ] as $i => $routeConfig ) {
if ( $routeConfig [ 'controller' ] == $targetController && $routeConfig [ 'action' ] == $targetAction ) {
unset ( $routeSourceConfig [ 'links' ][ $i ]);
$this -> endpoints [ $sourceController ][ $sourceAction ][ 'links' ] = $routeSourceConfig [ 'links' ];
break ;
}
}
}
}
public function getRouteConfig ( $controller , $action , $fullRoute = false )
{
$routeConfig = $this -> get ( $controller , $action );
if ( empty ( $fullRoute )) {
unset ( $routeConfig [ 'after' ]);
unset ( $routeConfig [ 'links' ]);
unset ( $routeConfig [ 'actions' ]);
}
return $routeConfig ;
}
private function execClosureIfNeeded ( $closure , $routeConfig = [])
{
if ( is_callable ( $closure )) {
return $closure ( $routeConfig );
}
return $closure ;
2021-09-09 11:05:00 +02:00
}
2021-10-08 10:08:02 +02:00
}