Plugin Directory

Changeset 3400983


Ignore:
Timestamp:
11/22/2025 02:59:07 PM (4 months ago)
Author:
plainware
Message:

2.0.6

Location:
plain-event-calendar/trunk
Files:
97 added
14 deleted
43 edited

Legend:

Unmodified
Added
Removed
  • plain-event-calendar/trunk/module/rest/boot.php

    r3394866 r3400983  
    77use Plainware\Ui\Component\Link;
    88use Plainware\Ui\PageMap;
     9
     10return function(App $app) {
     11    $app->autoload(__NAMESPACE__, __DIR__ . '/src/');
     12};
    913
    1014return function(App $app) {
     
    2428
    2529            ->whenDelete('api/event/{id}')
     30                ->middleware(Http\Middleware\CheckApiTokenMiddleware::class)
    2631                ->handle([Http\Api\EventApiController::class, 'deleteById'])
    2732            ;
     
    4247
    4348                ->when('admin/setting/rest')
    44                     ->makeTabRoot('__REST API__')
     49                    ->setTitle('__Rest API__')
    4550                    ->addTabItem((new Link('.'))->setLabel('__Details__'))
    4651                    ->addTabItem((new Link('./conf'))->setLabel('__Configuration__'))
  • plain-event-calendar/trunk/module/rest/src/Service/TokenManager.php

    r3394866 r3400983  
    33namespace PlainEventCalendar\Rest\Service;
    44
    5 use PlainEventCalendar\Setting\Repo\SettingRepo;
     5use PlainEventCalendar\Core\Repo\SettingRepo;
    66
    77class TokenManager
  • plain-event-calendar/trunk/openapi.yaml

    r3394866 r3400983  
    213213        title:
    214214          type: string
    215           example: Amazon
     215          example: Amazing meeting
    216216        description:
    217217          type: string
    218           example: It is gonna be an amazing event!
     218          example: It is going to be an amazing event!
    219219        startDate:
    220220          type: date
  • plain-event-calendar/trunk/plain-event-calendar.php

    r3394866 r3400983  
    33Plugin Name: Plain Event Calendar
    44Description: A simple event calendar to manage and publish your events.
    5 Version: 2.0.5
     5Version: 2.0.6
    66Author: plainware
    77Text Domain: plain-event-calendar
     
    4040        add_action('init', [$this, 'registerBlock']);
    4141
    42         add_action('rest_api_init', [$this, 'restInit']);
    43     }
    44 
    45     public function restInit()
     42        add_action('rest_api_init', [$this, 'wpRestInit']);
     43    }
     44
     45    public function wpRestInit()
    4646    {
    4747    // catch-all route
     
    5959    public function restHandle(\WP_REST_Request $wpRestRequest)
    6060    {
    61         $this->init();
     61        $this->bootRest();
    6262        $server = $this->app->make(\Plainware\HttpWordpress\WordpressServer::class);
    6363
     
    9999    }
    100100
    101     public function init()
     101    private function bootCore()
    102102    {
    103103        if ($this->app) {
     
    106106
    107107        $modules = [];
    108         $fs = ['include-wp.php', 'include.php', 'include-dev.php'];
     108        $fs = ['include-core.php', 'include-core-wp.php', 'include-dev.php'];
    109109        foreach ($fs as $f) {
    110110            $f = __DIR__ . '/' . $f;
     
    139139        $dbPrefix = $wpdbPrefix . $myDbPrefix . '_';
    140140
     141    // db prefix
    141142        $app->onBoot(\Plainware\Database\Database::class,
    142143            function ($db) use ($dbPrefix) {
     
    144145            });
    145146
     147        $this->app = $app;
     148    }
     149
     150    private function bootRest()
     151    {
     152        $this->bootCore();
     153        $modules = [];
     154        $fs = ['include-rest.php', 'include-rest-wp.php'];
     155        foreach ($fs as $f) {
     156            $f = __DIR__ . '/' . $f;
     157            if (file_exists($f)) {
     158                $modules = array_merge(require($f), $modules);
     159            }
     160        }
     161
     162        foreach ($modules as $moduleId) {
     163            $this->app->registerModule($moduleId);
     164        }
     165    }
     166
     167    private function bootWeb()
     168    {
     169        $this->bootCore();
     170        $modules = [];
     171        $fs = ['include-web-wp.php', 'include-web.php'];
     172        foreach ($fs as $f) {
     173            $f = __DIR__ . '/' . $f;
     174            if (file_exists($f)) {
     175                $modules = array_merge(require($f), $modules);
     176            }
     177        }
     178
     179        foreach ($modules as $moduleId) {
     180            $this->app->registerModule($moduleId);
     181        }
     182
    146183    // request prefix
    147184        $prefix = $this->slug;
    148         $app->onBoot(\Plainware\Http\Server::class,
     185        $this->app->onBoot(\Plainware\Http\Server::class,
    149186            function ($server) use ($prefix) {
    150187                $server->setPrefix($prefix);
     
    152189
    153190    // uri href func
    154         $app->onBoot(\Plainware\Http\Uri::class,
     191        $this->app->onBoot(\Plainware\Http\Uri::class,
    155192            function ($uri) {
    156193                $uri->setHrefFunc([$this, 'uriHref']);
    157194                $uri->setAssetFunc([$this, 'uriAsset']);
    158195            });
    159 
    160         $this->app = $app;
    161     }
    162 
    163     private function run()
    164     {
    165         $this->init();
     196    }
     197
     198    private function runWeb()
     199    {
     200        $this->bootWeb();
    166201        $server = $this->app->make(\Plainware\Http\Server::class);
    167202        $this->response = $server->run();
     
    173208            return;
    174209        }
    175 
    176210    // our page?
    177211        if ($this->slug != $_REQUEST['page']) {
     
    179213        }
    180214
    181         $this->run();
     215        $this->runWeb();
    182216
    183217   // has layout param? used for ajax partials, print views, downloads
     
    258292    {
    259293        if (null === $this->response) {
    260             $this->run();
     294            $this->runWeb();
    261295        }
    262296
     
    294328        }
    295329
    296         $uri = $this->uriAsset('module/app/assets/block.js');
     330        $uri = plugins_url('block.js', $this->pluginFile);
    297331        wp_register_script('pec_block', $uri, [
    298332            'wp-blocks',
     
    323357
    324358/*
    325 // sample hook code on domain events
    326 // see all events in module/app/src/Event
    327 add_action(\PlainEventCalendar\App\Event\EventRescheduled::class, function($domainEvent) {
    328     echo 'event id:' .
    329         esc_html($domainEvent->getEventId()) .
     359* sample hook code on any method call
     360
     361* LISTENER: called after the default function. Syntax: '__FUNC_NAME__'
     362
     363add_action(\PlainEventCalendar\Core\Action\RescheduleEventAction::class, function($event, $d1, $d2) {
     364    echo 'event rescheduled:' .
     365        esc_html($event->getId()) .
    330366        'has been rescheduled to ' .
    331         esc_html($domainEvent->getNewStartDate());
    332 });
     367        esc_html($d1->format('Y-m-d') . ' - ' . $d2->format('Y-m-d'));
     368        // return $ret;
     369}, 10, 3);
     370
     371* modifier: called after the default function, gets its return in 1st argument. Syntax: '__FUNC_NAME__()'
     372add_filter('(' . \PlainEventCalendar\WebAdmin\Html\EventPresenter::class . '::getTitle)', function(callable $next, $event) {
     373    $ret = 'wrap (' . $next($event) . ')';
     374    return $ret;
     375}, 10, 2);
     376
     377* decorator: wraps the default function, gets callable in first argument. Syntax: '(__FUNC_NAME__)'
     378add_filter(\PlainEventCalendar\WebAdmin\Html\EventPresenter::class . '::getTitle()', function($ret, $event) {
     379    $ret = 'append ' . $ret;
     380    return $ret;
     381}, 10, 2);
    333382*/
     383
  • plain-event-calendar/trunk/readme.txt

    r3394866 r3400983  
    33Tags: event calendar, event manager, calendar, events, school, university, church
    44License: GPLv2 or later
    5 Stable tag: 2.0.5
     5Stable tag: 2.0.6
    66Requires at least: 4.1
    77Tested up to: 6.8
     
    4949== Changelog ==
    5050
     51= 2.0.6 (2025-11-21) =
     52* Experimenting with the new module structure.
     53* Framework code update.
     54
    5155= 2.0.5 (2025-11-13) =
    5256* Added REST API interface.
  • plain-event-calendar/trunk/vendor/plainware/core/src/App.php

    r3394866 r3400983  
    3636    private array $listener = [];
    3737
     38// cache to keep callables
     39    private $getListenersFunc = null;
     40    private $getDecoratorsFunc = null;
     41
    3842    public function __construct(string $mainFile, array $env = [])
    3943    {
     
    111115            $moduleBoot = require($moduleBootFile) ?? null;
    112116            if (is_callable($moduleBoot)) {
    113                 call_user_func($moduleBoot, $this);
     117                $moduleBoot($this);
    114118            }
    115119        }
     
    161165    }
    162166
     167    public function getListeners(string $funcId): array
     168    {
     169        return $this->getListenersFunc()($funcId);
     170    }
     171
     172    private function getListenersFunc(): callable
     173    {
     174        if (null === $this->getListenersFunc) {
     175            $func = function(string $funcId): array {
     176                return $this->listener[$funcId] ?? [];
     177            };
     178
     179            $decorators = $this->decorator[__CLASS__ . '::getListeners'] ?? [];
     180            foreach ($decorators as $decorator) {
     181                $func = function (...$args) use ($decorator, $func) {
     182                    $args[] = $func;
     183                    $decorator = $this->makeFunc($decorator);
     184
     185                    return $decorator(...$args);
     186                };
     187            }
     188
     189            $this->getListenersFunc = $func;
     190        }
     191
     192        return $this->getListenersFunc;
     193    }
     194
     195    public function getDecorators(string $funcId): array
     196    {
     197        return $this->getDecoratorsFunc()($funcId);
     198    }
     199
     200    private function getDecoratorsFunc(): callable
     201    {
     202        if (null === $this->getDecoratorsFunc) {
     203            $func = function(string $funcId): array {
     204                return $this->decorator[$funcId] ?? [];
     205            };
     206
     207            $decorators = $this->decorator[__CLASS__ . '::getDecorators'] ?? [];
     208            foreach ($decorators as $decorator) {
     209                $func = function (...$args) use ($decorator, $func) {
     210                    $decorator = $this->makeFunc($decorator);
     211                    $args[] = $func;
     212
     213                    return $decorator(...$args);
     214                };
     215            }
     216
     217            $this->getDecoratorsFunc = $func;
     218        }
     219
     220        return $this->getDecoratorsFunc;
     221    }
     222
    163223    public function call($func, array $args = [])
    164224    {
    165         $func = $this->makeFunc($func);
    166 
    167         return call_user_func_array($func, $args);
    168     }
    169 
    170 // register a decorator function: one that wraps an original func
    171     public function decorate($original, $decorator): self
    172     {
    173         if (is_array($original)) {
    174             $original = join('::', $original);
    175         }
    176 
    177         if (!isset($this->decorator[$original])) {
    178             $this->decorator[$original] = [];
    179         }
    180         $this->decorator[$original][] = $decorator;
    181 
    182         return $this;
    183     }
    184 
    185 // register a listener function: one that runs after the original func, gets its return value as 1st arg
    186     public function listen($original, $listener): self
    187     {
    188         if (is_array($original)) {
    189             $original = join('::', $original);
    190         }
    191 
    192         if (!isset($this->listener[$original])) {
    193             $this->listener[$original] = [];
    194         }
    195         $this->listener[$original][] = $listener;
    196 
    197         return $this;
    198     }
    199 
    200 // bind already instantiated object
    201     public function bind(string $class, object $instance)
    202     {
    203         if (!($instance instanceof $class)) {
    204             throw new \InvalidArgumentException(get_class($instance) . ' should be ' . $class);
    205         }
    206         $this->made[$class] = $instance;
    207 
    208         return $this;
    209     }
    210 
    211     public function makePipeline(array $funcs): callable
    212     {
    213         $func = array_shift($funcs);
    214         $func = $this->makeFunc($func);
    215 
    216         foreach ($funcs as $decorator) {
    217             $decorator = $this->makeFunc($decorator);
    218             $func = function (...$args) use ($decorator, $func) {
    219                 $args[] = $func;
    220                 return call_user_func_array($decorator, $args);
    221             };
    222         }
    223 
    224         return $func;
     225        return $this->makeFunc($func)(...$args);
    225226    }
    226227
     
    242243        }
    243244
    244     // decorators? build chain of functions, each one gets previus func appended to args list
    245 // echo "CHECK '$funcName'<br>";
    246 // _print_r(array_keys($this->decorators));
    247         list($className, $methodName) = is_array($func) ? $func : [$func, null];
    248         if (is_object($className)) {
    249             $className = get_class($className);
    250         }
    251         $funcName = (null === $methodName) ? $className : $className . '::' . $methodName;
    252 
    253         if (isset($this->decorator[$funcName])) {
    254             foreach ($this->decorator[$funcName] as $decoratorFunc) {
    255                 $decoratorFunc = $this->makeFunc($decoratorFunc);
    256                 $func = function (...$args) use ($decoratorFunc, $func) {
     245        $funcId = $this->getFuncId($func);
     246
     247    // decorators?
     248        if ((null !== $funcId) && ($decorators = $this->getDecorators($funcId))) {
     249            foreach ($decorators as $decorator) {
     250                $func = function (...$args) use ($decorator, $func) {
     251                    $decorator = $this->makeFunc($decorator);
    257252                    $args[] = $func;
    258                     return call_user_func_array($decoratorFunc, $args);
     253
     254                    return $decorator(...$args);
    259255                };
    260256            }
     257        }
     258
     259    // listeners?
     260        if ((null !== $funcId) && ($listeners = $this->getListeners($funcId))) {
     261            $func = function (...$args) use ($listeners, $func) {
     262                $ret = $func(...$args);
     263                foreach ($listeners as $listener) {
     264                    $listener = $this->makeFunc($listener);
     265
     266                    return $listener(...$args);
     267                }
     268                return $ret;
     269            };
     270        }
     271
     272        return $func;
     273    }
     274
     275    private function getFuncId($func): ?string
     276    {
     277        if ($func instanceof \Closure) {
     278            return null;
     279        }
     280
     281        if (is_array($func)) {
     282            $ret = is_object($func[0]) ? get_class($func[0]) : $func[0];
     283            $ret .= '::' . $func[1];
     284        } else {
     285            $ret = is_object($func) ? get_class($func) : $func;
     286        }
     287
     288        return $ret;
     289    }
     290
     291// register a decorator function: one that wraps an original func
     292    public function decorate($original, $decorator): self
     293    {
     294        if (is_array($original)) {
     295            $original = join('::', $original);
     296        }
     297
     298        if (!isset($this->decorator[$original])) {
     299            $this->decorator[$original] = [];
     300        }
     301        $this->decorator[$original][] = $decorator;
     302
     303        return $this;
     304    }
     305
     306// register a listener function: one that runs after the original func, gets its return value as 1st arg
     307    public function listen($original, $listener): self
     308    {
     309        if (is_array($original)) {
     310            $original = join('::', $original);
     311        }
     312
     313        if (!isset($this->listener[$original])) {
     314            $this->listener[$original] = [];
     315        }
     316        $this->listener[$original][] = $listener;
     317
     318        return $this;
     319    }
     320
     321// bind already instantiated object
     322    public function bind(string $class, object $instance)
     323    {
     324        if (!($instance instanceof $class)) {
     325            throw new \InvalidArgumentException(get_class($instance) . ' should be ' . $class);
     326        }
     327        $this->made[$class] = $instance;
     328
     329        return $this;
     330    }
     331
     332    public function makePipeline(array $funcs): callable
     333    {
     334        $func = array_shift($funcs);
     335        $func = $this->makeFunc($func);
     336
     337        foreach ($funcs as $decorator) {
     338            $func = function (...$args) use ($decorator, $func) {
     339                $args[] = $func;
     340                $decorator = $this->makeFunc($decorator);
     341
     342                return $decorator(...$args);
     343            };
    261344        }
    262345
  • plain-event-calendar/trunk/vendor/plainware/core/src/ObjectProxy.php

    r3394866 r3400983  
    1414    }
    1515
     16    // public function __toString()
     17    // {
     18        // return $this->className;
     19    // }
     20
    1621    public function __invoke()
    1722    {
     
    2227    {
    2328        return call_user_func($this->objectMethodCaller, [$this->className, $methodName], $args);
    24         return $GLOBALS[$this->appGlobalVarName]->call([$this->className, $methodName], $args);
    2529    }
    2630}
  • plain-event-calendar/trunk/vendor/plainware/database-wordpress/src/WordpressUserHelper.php

    r3372822 r3400983  
    5353    }
    5454
     55    public static function getUserRolesById(int $userId): array
     56    {
     57        static $cache = [];
     58
     59        if (!isset($cache[$userId])) {
     60            $ret = [];
     61
     62            $wpUser = get_user_by('id', $userId);
     63            if ($wpUser) {
     64                $ret = static::getUserRoles($wpUser);
     65            }
     66
     67            $cache[$userId] = $ret;
     68        }
     69
     70        return $cache[$userId];
     71    }
     72
    5573    public static function getWordpressRole($userId)
    5674    {
  • plain-event-calendar/trunk/vendor/plainware/database/boot.php

    r3394866 r3400983  
    44
    55use Plainware\Core\App;
    6 use Plainware\Core\EventBus;
    76use Plainware\Database\App\AppOnEnd;
    87
     
    109    $app->autoload(__NAMESPACE__, __DIR__ . '/src/');
    1110    $app->onEnd(AppOnEnd::class);
    12     $app->onBoot(EventBus::class, function ($eventBus) {
    13         $eventBus->defer();
    14     });
    1511};
  • plain-event-calendar/trunk/vendor/plainware/database/src/App/AppOnEnd.php

    r3394866 r3400983  
    33namespace Plainware\Database\App;
    44
    5 use Plainware\App\EventBus;
    65use Plainware\Core\App;
    76use Plainware\Database\Database;
     
    109{
    1110    public $db = Database::class;
    12     public $eventBus = EventBus::class;
    1311
    1412    public function __invoke()
    1513    {
    1614        $this->db->flush();
    17         while($this->eventBus->hasEvents()) {
    18             $this->eventBus->processEvents();
    19             $this->db->flush();
    20         }
    2115    }
    2216}
  • plain-event-calendar/trunk/vendor/plainware/database/src/Database.php

    r3394866 r3400983  
    1313    private array $order = [];
    1414    private array $fields = [];
     15    private bool $distinct = false;
    1516    private array $tables = [];
    1617    private array $join = [];
     
    130131    }
    131132
     133    public function distinct(bool $on): self
     134    {
     135        $this->distinct = $on;
     136
     137        return $this;
     138    }
     139
    132140    public function get(): array
    133141    {
     
    144152
    145153        $sql .= 'SELECT';
     154        if ($this->distinct) {
     155            $sql .= ' DISTINCT';
     156        }
     157
    146158        // $sql .= 'SELECT ' . ($this->fields ? $this->buildIdentifiers(array_keys($this->fields)) : '*');
    147159        if ($this->fields) {
     
    194206        $this->tables = $this->where = $this->join = $this->fields = $this->order = [];
    195207        $this->limit = $this->offset = 0;
     208        $this->distinct = false;
    196209
    197210        return $this->query($sql, $args);
     
    221234        }
    222235
     236    // reset
     237        $this->tables = $this->where = $this->join = $this->fields = $this->order = [];
     238        $this->distinct = false;
     239
    223240        return $ret;
    224241    }
     
    238255
    239256    // reset
    240         $this->tables = $this->where = $this->fields = [];
     257        $this->tables = $this->where = $this->join = $this->fields = $this->order = [];
    241258
    242259        return $this->query($sql, $args);
     
    252269        }
    253270
    254         $names = array_keys($this->fields);
    255         $values = array_values($this->fields);
     271    // don't need nulls
     272        $fields = $this->fields;
     273        $fields = array_filter($fields, fn($e) => (null === $e) ? false : true);
     274        if (!$fields) {
     275            throw new \InvalidArgumentException('Need fields to INSERT');
     276        }
     277
     278        $names = array_keys($fields);
     279        $values = array_values($fields);
    256280
    257281        $sql = '';
     
    262286
    263287    // reset
    264         $this->tables = $this->fields = [];
     288        $this->tables = $this->where = $this->join = $this->fields = $this->order = [];
    265289
    266290        return $this->query($sql, $args);
     
    297321            $sqlSet = [];
    298322            foreach ($this->fields as $name => $value) {
    299                 $sqlSet[] = '`' . $name . '`=' . $this->buildPlaceholder($value);
    300                 $args[] = $value;
     323                if (null === $value) {
     324                    $sqlSet[] = '`' . $name . '`=NULL';
     325                } else {
     326                    $sqlSet[] = '`' . $name . '`=' . $this->buildPlaceholder($value);
     327                    $args[] = $value;
     328                }
    301329            }
    302330            $sql .= ' SET ' . join(', ', $sqlSet);
     
    307335
    308336    // reset
    309         $this->tables = $this->fields = $this->where = [];
     337        $this->tables = $this->where = $this->join = $this->fields = $this->order = [];
    310338
    311339        return $ret;
     
    322350                $sqlSet = [];
    323351                foreach ($fields as $name => $value) {
    324                     $sqlSet[] = '`' . $name . '`=' . $this->buildPlaceholder($value);
    325                     $args[] = $value;
     352                    if (null === $value) {
     353                        $sqlSet[] = '`' . $name . '`=NULL';
     354                    } else {
     355                        $sqlSet[] = '`' . $name . '`=' . $this->buildPlaceholder($value);
     356                        $args[] = $value;
     357                    }
    326358                }
    327359                $sql .= ' SET ' . join(', ', $sqlSet);
  • plain-event-calendar/trunk/vendor/plainware/datetime/src/DateTimeFormatter.php

    r3394866 r3400983  
    4242    }
    4343
     44    public function getWeekStartsOn(): int
     45    {
     46        return $this->weekStartsOn;
     47    }
     48
     49    public function setWeekStartsOn(int $v): self
     50    {
     51        $this->weekStartsOn = $v;
     52
     53        return $this;
     54    }
     55
     56    public function getStartWeek(\DateTimeInterface $dt): \DateTimeInterface
     57    {
     58        $dayOfWeek = $this->getDayOfWeek($dt);
     59        $diff = $dayOfWeek - $this->weekStartsOn;
     60
     61        $ret = $dt;
     62        if ($diff > 0) {
     63            $ret = $ret->modify('-' . $diff  . ' day');
     64        } elseif ($diff < 0) {
     65            $ret = $ret->modify('-' . (7 - (-$diff))  . ' day');
     66        }
     67        $ret = $ret->setTime(0, 0);
     68
     69        return $ret;
     70    }
     71
     72    private function getDayOfWeek(\DateTimeInterface $dt): int
     73    {
     74        $ret = $dt->format('w');
     75    // sunday?
     76        if (0 == $ret) {
     77            $ret = 7;
     78        }
     79
     80        return $ret;
     81    }
     82
    4483    public function formatDateFull(\DateTimeInterface $dt, ?string $format = null): string
    4584    {
    46         return $dt->format($format ?? $this->dateFormat);
     85        $dayOfWeek = $this->getDayOfWeek($dt);
     86        $dayOfWeekLabel = static::$dayOfWeekNames[$dayOfWeek - 1];
     87
     88        return $dayOfWeekLabel . ', ' . $dt->format($format ?? $this->dateFormat);
    4789    }
    4890
     
    82124    }
    83125
     126    public function formatHour(\DateTimeInterface $dt, ?string $format = null): string
     127    {
     128        $ret = $this->formatTime($dt, $format);
     129        $pos = strpos($ret, ':');
     130        if (false !== $pos) {
     131            $ret = substr_replace($ret, '', $pos, 3);
     132        }
     133
     134        return $ret;
     135    }
     136
    84137    public function getMonthNames(): array
    85138    {
  • plain-event-calendar/trunk/vendor/plainware/http-wordpress/src/WordpressServer.php

    r3394866 r3400983  
    4141
    4242        $routeParams = $wpRestRequest->get_url_params();
    43         foreach ($routeParams as $k => $v) {
    44             $request = $request->withRouteParam($k, $v);
    45         }
     43        $request = $request->withRouteParams($routeParams);
     44
     45        $headers = $wpRestRequest->get_headers();
     46        $request = $request->withHeaders($headers);
    4647
    4748        $wpRoute = $wpRestRequest->get_route();
    4849        $wpRoute = ltrim($wpRoute, '/');
    49         $ourRoute = $ourRouteNamespace . substr($wpRoute, strlen($wpRouteNamespace));
    50         $ourRoute = ltrim($ourRoute, '/');
     50        $ourPath = $ourRouteNamespace . substr($wpRoute, strlen($wpRouteNamespace));
     51        $ourPath = ltrim($ourPath, '/');
    5152
    52         $request = $request->withSlug($ourRoute);
     53        $request = $request->withPath($ourPath);
    5354
    5455    // try body params
  • plain-event-calendar/trunk/vendor/plainware/http/src/Redirect.php

    r3394866 r3400983  
    55class Redirect
    66{
    7     private string $slug = '';
     7    private string $path = '';
    88    private array $params = [];
    9     private string $message = '';
     9    private array $messages = [];
    1010
    11     public function __construct(string $slug, array $params = [])
     11    public function __construct(string $path, array $params = [])
    1212    {
    13         $this->slug = $slug;
     13        $this->path = $path;
    1414        $this->params = $params;
    1515    }
    1616
    17     public function getSlug(): string
     17    public function getPath(): string
    1818    {
    19         return $this->slug;
     19        return $this->path;
    2020    }
    2121
     
    3333    }
    3434
    35     public function withMessage(string $message = '1')
     35    public function withMessage(string $message)
    3636    {
    3737        $ret = clone $this;
    38         $ret->message = $message;
     38        $ret->messages[] = $message;
    3939
    4040        return $ret;
    4141    }
    4242
    43     public function getMessage(): string
     43    public function getMessages(): array
    4444    {
    45         return $this->message;
     45        return $this->messages;
    4646    }
    4747}
  • plain-event-calendar/trunk/vendor/plainware/http/src/Request.php

    r3394866 r3400983  
    66{
    77    private string $requestMethod;
     8    private array $postParams = [];
    89    private array $queryParams = [];
    910    private array $routeParams = [];
     
    1213    private $parsedBody = [];
    1314    private array $attributes = [];
    14     private string $route = '';
     15    private string $path = '';
     16    private ?string $routePattern = null;
    1517    private string $prefix = '';
    16     private $currentUserId = null;
    17 
    18     private static string $passParamPrefix = '-';
    19     private static string $tempParamSuffix = '-';
    2018
    2119    public function __construct($prefix = '')
    2220    {
    2321        $this->prefix = $prefix;
    24 
    25         $requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'GET';
    26         $this->requestMethod = strtoupper($requestMethod);
    27 
    28         $this->fromGlobals();
    2922    }
    3023
     
    3427    }
    3528
     29    public function withBody($v): self
     30    {
     31        $ret = clone $this;
     32        $ret->body = $v;
     33
     34        return $ret;
     35    }
     36
    3637    public function getParsedBody(): array
    3738    {
     
    3940    }
    4041
    41     public function withParsedBody(array $data)
     42    public function withParsedBody(array $data): self
    4243    {
    4344        $ret = clone $this;
     
    7677    }
    7778
    78     public function getSlug(): string
    79     {
    80         return $this->getRoute();
    81     }
    82 
    83     public function getRoute(): string
    84     {
    85         return $this->route;
     79    public function getPath(): string
     80    {
     81        return $this->path;
     82    }
     83
     84    public function getRoutePattern(): ?string
     85    {
     86        return $this->routePattern;
    8687    }
    8788
    8889    public function getPostParam($k)
    8990    {
    90         $paramName = $this->getParamName($k);
    91         $ret = $_POST[$paramName] ?? null;
     91        return $this->postParams[$k] ?? null;
     92    }
     93
     94    public function withPostParams(array $params): self
     95    {
     96        $ret = clone $this;
     97        $ret->postParams = $params;
    9298
    9399        return $ret;
     
    108114        $ret = clone $this;
    109115        $ret->headers[$this->prepareHeaderName($name)] = $value;
     116
     117        return $ret;
     118    }
     119
     120    public function withHeaders(array $vs): self
     121    {
     122        $ret = clone $this;
     123
     124        $ret->headers = [];
     125        foreach ($vs as $name => $value) {
     126            while (is_array($value)) {
     127                $value = current($value);
     128            }
     129            $ret->headers[$this->prepareHeaderName($name)] = $value;
     130        }
    110131
    111132        return $ret;
     
    153174    }
    154175
    155     private function fromGlobals(): self
    156     {
    157     // headers
    158         foreach ($_SERVER as $name => $value) {
    159             if (substr($name, 0, 5) == 'HTTP_') {
    160                 // $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
    161                 $name = substr($name, strlen('HTTP_'));
    162                 $this->headers[$this->prepareHeaderName($name)] = $value;
    163             }
    164         }
    165 
    166     // query params
    167         $ret = [];
    168         $names = array_keys($_GET);
    169         if (!$names) {
    170             return $this;
    171         }
    172 
    173         if ($this->prefix) {
    174             $iis = array_keys($names);
    175             foreach ($iis as $ii) {
    176                 if ($this->prefix === substr($names[$ii], 0, strlen($this->prefix))) {
    177                     $names[$ii] = substr($names[$ii], strlen($this->prefix));
    178                 } else {
    179                     unset($names[$ii]);
    180                 }
    181             }
    182         }
    183         if (!$names) {
    184             return $this;
    185         }
    186 
    187         foreach ($names as $name) {
    188             $paramName = $this->getParamName($name);
    189             $ret[$name] = $_GET[$paramName] ?? null;
    190         }
    191 
    192         $this->queryParams = $ret;
    193 
    194     // body
    195         $this->body = file_get_contents('php://input');
    196 
    197     // json params
    198         if (strlen($this->body)){
    199             $parsedBody = json_decode($this->body, true);
    200             if ((null === $parsedBody) && (JSON_ERROR_NONE !== json_last_error())) {
    201                 // Ensure subsequent calls receive error instance.
    202                 // $this->parsed_json = false;
    203 
    204                 $error = [
    205                     'json_error_code'    => json_last_error(),
    206                     'json_error_message' => json_last_error_msg(),
    207                 ];
    208 
    209                 $parsedBody = [];
    210                 // return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data );
    211             }
    212             $this->parsedBody = $parsedBody;
    213         }
    214 
    215         return $this;
    216     }
    217 
    218176    public function getQueryParams(): array
    219177    {
     
    229187    }
    230188
    231     public function withSlug(string $v): self
    232     {
    233         $ret = clone $this;
    234         $ret->route = $v;
     189    public function withPath(string $v): self
     190    {
     191        $ret = clone $this;
     192        $ret->path = $v;
     193
     194        return $ret;
     195    }
     196
     197    public function withRoutePattern(?string $v): self
     198    {
     199        $ret = clone $this;
     200        $ret->routePattern = $v;
    235201
    236202        return $ret;
     
    241207        $ret = clone $this;
    242208        $ret->routeParams[$k] = $v;
     209
     210        return $ret;
     211    }
     212
     213    public function withRouteParams(array $params): self
     214    {
     215        $ret = clone $this;
     216        $ret->routeParams = $params;
    243217
    244218        return $ret;
     
    261235    }
    262236
    263     // public function withQueryParam(string $k, $v): self
     237    public function resolvePath(string $path): string
     238    {
     239        $pathArray = strlen($path) ? explode('/', $path) : [];
     240        $currentPath = $this->getPath();
     241        $currentPathArray = strlen($currentPath) ? explode('/', $currentPath) : [];
     242
     243        $copyCurrentPathArray = $currentPathArray;
     244        $ret = [];
     245        while ($p = array_shift($pathArray)) {
     246            if ('.' === $p) {
     247                $ret = $currentPathArray;
     248            } elseif ('..' === $p) {
     249                array_pop($copyCurrentPathArray);
     250                $ret = $copyCurrentPathArray;
     251            } else {
     252                $ret[] = $p;
     253            }
     254        }
     255
     256        $ret = join('/', $ret);
     257
     258        return $ret;
     259    }
     260
     261    // public function toString(): string
    264262    // {
    265         // $this->queryParams[$k] = $v;
    266         // return $this;
    267 
    268         // $ret = clone $this;
    269         // $ret->queryParams[$k] = $v;
     263        // $ret = $this->getRoute();
     264        // $params = $this->getQueryParams();
     265        // if ($params) {
     266            // $paramsString = http_build_query($params);
     267            // if ($paramsString) {
     268                // $ret .= $paramsString;
     269            // }
     270        // }
    270271
    271272        // return $ret;
    272273    // }
    273 
    274     public function resolveRoute(string $route, string $currentRoute)
    275     {
    276         $routeArray = strlen($route) ? explode('/', $route) : [];
    277        
    278         $currentRouteArray = explode('/', $currentRoute);
    279         $currentRouteArray = strlen($currentRoute) ? explode('/', $currentRoute) : [];
    280 
    281         $finalRouteArray = [];
    282         $copyCurrentRouteArray = $currentRouteArray;
    283 
    284         while ($p = array_shift($routeArray)) {
    285             if ('.' === $p) {
    286                 $finalRouteArray = $currentRouteArray;
    287             } elseif ('..' === $p) {
    288                 array_pop($copyCurrentRouteArray);
    289                 $finalRouteArray = $copyCurrentRouteArray;
    290             } else {
    291                 $finalRouteArray[] = $p;
    292             }
    293         }
    294 
    295         $ret = join('/', $finalRouteArray);
    296 
    297         return $ret;
    298     }
    299 
    300     public function makeTo(string $route, array $params = []): self
    301     {
    302         $currentRoute = $this->getRoute();
    303         $currentParams = $this->getQueryParams();
    304 
    305         $finalRoute = $this->resolveRoute($route, $currentRoute);
    306 
    307         $passParams = $this->passParams($currentRoute, $finalRoute, $currentParams);
    308         foreach ($passParams as $k => $v) {
    309             if (!array_key_exists($k, $params)) {
    310             // if (!isset($params[$k]) ){
    311                 $params[$k] = $v;
    312             }
    313         }
    314 
    315         // $routeParams = $this->routeParams;
    316         // if ($routeParams) {
    317             // $routeParams = $this->passParams($currentSlug, $finalSlug, $routeParams);
    318         // }
    319 
    320         $routeParams = [];
    321         $ret = clone $this;
    322         $ret->route = $finalRoute;
    323         $ret->queryParams = $params;
    324         $ret->routeParams = $routeParams;
    325 
    326         return $ret;
    327     }
    328 
    329     public function getCurrentUserId()
    330     {
    331         return $this->currentUserId;
    332     }
    333 
    334     public function withCurrentUserId($id): self
    335     {
    336         $ret = clone $this;
    337         $ret->currentUserId = $id;
    338 
    339         return $ret;
    340     }
    341 
    342     private function isParamTemporary(string $paramName): bool
    343     {
    344         if (static::$tempParamSuffix === substr($paramName, -strlen(static::$tempParamSuffix))) {
    345             $ret = true;
    346         } else {
    347             $ret = false;
    348         }
    349 
    350         return $ret;
    351     }
    352 
    353     private function isParamPermanent(string $paramName): bool
    354     {
    355         $suffix = '*';
    356 
    357         if ($suffix === substr($paramName, -strlen($suffix))) {
    358             $ret = true;
    359         } else {
    360             $ret = false;
    361         }
    362 
    363         return $ret;
    364     }
    365 
    366     private function passParams(string $fromSlug, string $toSlug, array $params)
    367     {
    368         $ret = [];
    369 
    370     /* keep temp params? */
    371         $keepTemp = false;
    372         if (array_key_exists(static::$tempParamSuffix, $params) && (true === $params[static::$tempParamSuffix])) {
    373             $keepTemp = true;
    374             unset($params[static::$tempParamSuffix]);
    375         }
    376 
    377         if (!$keepTemp) {
    378             $ks = array_keys($params);
    379             foreach ($ks as $k) {
    380                 if ($this->isParamTemporary($k)) {
    381                     // echo "UNSET '$k'<br>";
    382                     unset($params[$k]);
    383                 }
    384             }
    385         }
    386 
    387     // permanent params?
    388         $ks = array_keys($params);
    389         foreach ($ks as $k) {
    390             if ($this->isParamPermanent($k)) {
    391                 $ret[$k] = $params[$k];
    392                 unset($params[$k]);
    393             }
    394         }
    395 
    396         $stepsToSlug = static::determineStepsToOtherSlug($fromSlug, $toSlug);
    397 
    398         if (null === $stepsToSlug) {
    399         // probably can have a common parent
    400             $commonParent = static::determineCommonParent($fromSlug, $toSlug);
    401 
    402             if ($commonParent) {
    403                 // echo "'$fromSlug' : '$toSlug'  = '$commonParent'<br>";
    404                 $commonParentParams = $this->passParams($fromSlug, $commonParent, $params);
    405                 $ret = $this->passParams($commonParent, $toSlug, $commonParentParams);
    406             }
    407         } elseif (0 === $stepsToSlug) {
    408             $ret = $params;
    409     // pass to child
    410         } elseif ($stepsToSlug > 0) {
    411             foreach ($params as $k => $v) {
    412                 $k2 = str_repeat(static::$passParamPrefix, $stepsToSlug) . $k;
    413                 $ret[$k2] = $v;
    414             }
    415     // pass to parent
    416         } elseif ($stepsToSlug < 0) {
    417             foreach ($params as $k => $v) {
    418                 $prefix = str_repeat(static::$passParamPrefix, -$stepsToSlug);
    419                 if ($prefix === substr($k, 0, strlen($prefix))) {
    420                     $k2 = substr($k, strlen($prefix));
    421                     $ret[$k2] = $v;
    422                 }
    423             }
    424         }
    425 
    426         return $ret;
    427     }
    428 
    429     private static function determineCommonParent(string $fromSlug, string $toSlug): string
    430     {
    431     // the same slug
    432         if ($fromSlug === $toSlug) {
    433             return $fromSlug;
    434         }
    435 
    436         $fromSlugArray = explode('/', $fromSlug);
    437         $toSlugArray = explode('/', $toSlug);
    438 
    439         $ret = '';
    440 
    441         $len = min(count($fromSlugArray), count($toSlugArray));
    442         $matchLen = 0;
    443         for ($i = 0; $i < $len; $i++) {
    444             if ($fromSlugArray[$i] === $toSlugArray[$i]) {
    445                 $matchLen = $i + 1;
    446             } else {
    447                 break;
    448             }
    449         }
    450 
    451         if (!$matchLen) {
    452             return $ret;
    453         }
    454 
    455         $ret = array_slice($fromSlugArray, 0, $matchLen);
    456         $ret = join('/', $ret);
    457 
    458         return $ret;
    459     }
    460 
    461     private static function determineStepsToOtherSlug(string $fromSlug, string $toSlug): ?int
    462     {
    463     // the same slug
    464         if ($fromSlug === $toSlug) {
    465             return 0;
    466         }
    467 
    468         $fromSlugArray = explode('/', $fromSlug);
    469         $toSlugArray = explode('/', $toSlug);
    470 
    471         $countFrom = count($fromSlugArray);
    472         $countTo = count($toSlugArray);
    473 
    474     // toSlug is child
    475         if ($countTo > $countFrom) {
    476             $ok = true;
    477             for ($i = 0; $i < $countFrom; $i++) {
    478                 if ($fromSlugArray[$i] !== $toSlugArray[$i]) {
    479                     $ok = false;
    480                     break;
    481                 }
    482             }
    483 
    484             return $ok ? ($countTo - $countFrom) : null;
    485         }
    486 
    487     // toSlug is parent
    488         if ($countTo < $countFrom) {
    489             $ok = true;
    490             for ($i = 0; $i < $countTo; $i++) {
    491                 if ($fromSlugArray[$i] !== $toSlugArray[$i]) {
    492                     $ok = false;
    493                     break;
    494                 }
    495             }
    496 
    497             return $ok ? -($countFrom - $countTo) : null;
    498         }
    499 
    500         return null;
    501     }
    502 
    503     public function toString(): string
    504     {
    505         $ret = $this->getSlug();
    506         $params = $this->getQueryParams();
    507         if ($params) {
    508             $paramsString = http_build_query($params);
    509             if ($paramsString) {
    510                 $ret .= $paramsString;
    511             }
    512         }
    513 
    514         return $ret;
    515     }
    516274}
  • plain-event-calendar/trunk/vendor/plainware/http/src/Router.php

    r3394866 r3400983  
    1515    private string $tempRoute = '';
    1616    private string $tempMethod = '';
     17    private array $parsedCache = [];
    1718
    1819    private function getAllBridges(): array
     
    4647 * @return array
    4748 */
    48     public function parseRoute(string $route): array
    49     {
    50         foreach (array_keys($this->handler) as $routePattern) {
    51             list($matchedRoute, $params) = $this->getParsedRouteWithParams($route, $routePattern);
    52             if (false !== $matchedRoute) {
    53                 return [$routePattern, $params];
    54             }
    55         }
    56 
    57     // direct routes not found, try bridged ones
    58         $bridgedHanlers = $this->getAllBridges();
    59         foreach (array_keys($bridgedHanlers) as $routePattern) {
    60             list($matchedRoute, $params) = $this->getParsedRouteWithParams($route, $routePattern);
    61             if (false !== $matchedRoute) {
    62                 return [$routePattern, $params];
    63             }
    64         }
    65 
    66         return [false, []];
     49    public function parsePath(string $path): array
     50    {
     51        if (array_key_exists($path, $this->parsedCache)) {
     52            return $this->parsedCache[$path];
     53        }
     54
     55        $ret = null;
     56
     57        if (!strlen($path)) {
     58            $ret = [$path, []];
     59        }
     60
     61        if (null === $ret) {
     62            foreach (array_keys($this->handler) as $routePattern) {
     63                $params = $this->getRouteParams($path, $routePattern);
     64                if (null !== $params) {
     65                    $ret = [$routePattern, $params];
     66                    break;
     67                }
     68            }
     69        }
     70
     71        if (null === $ret) {
     72        // direct routes not found, try bridged ones
     73            $bridgedHanlers = $this->getAllBridges();
     74            foreach (array_keys($bridgedHanlers) as $routePattern) {
     75                $params = $this->getRouteParams($path, $routePattern);
     76                if (null !== $params) {
     77                    $ret = [$routePattern, $params];
     78                    break;
     79                }
     80            }
     81        }
     82
     83        if (null === $ret) {
     84            $ret = [null, []];
     85        }
     86
     87        $this->parsedCache[$path] = $ret;
     88
     89        return $ret;
    6790    }
    6891
     
    77100 * @return array ['user/12/order/24', ['user' => 12, 'order' => 24]]
    78101 */
    79     private function getParsedRouteWithParams(string $route, string $routePattern, bool $asPrefix = false)
    80     {
    81         if ($route === $routePattern) {
    82             return [$route, []];
    83         }
    84 
    85         $routeArray = strlen($route) ? explode('/', $route) : [];
     102    public function getRouteParams(string $path, string $routePattern): ?array
     103    {
     104        if ($path === $routePattern) {
     105            return [];
     106        }
     107
     108        $pathArray = strlen($path) ? explode('/', $path) : [];
    86109        $routePatternArray = strlen($routePattern) ? explode('/', $routePattern) : [];
    87110
    88         $len = count($routeArray);
     111        $len = count($pathArray);
    89112        $lenPattern = count($routePatternArray);
    90113
    91         if (!$asPrefix) {
    92             if ($lenPattern !== $len) {
    93                 return [false, []];
    94             }
     114        if ($lenPattern !== $len) {
     115            return null;
    95116        }
    96117
     
    100121            if ('{' === substr($routePatternArray[$i], 0, 1)) {
    101122                $paramName = substr($routePatternArray[$i], 1, -1);
    102                 $params[$paramName] = $routeArray[$i];
     123                $params[$paramName] = $pathArray[$i];
    103124            } else {
    104                 if ($routeArray[$i] !== $routePatternArray[$i]) {
    105                     return [false, []];
    106                 }
    107             }
    108         }
    109 
    110         $retRoute = join('/', array_slice($routeArray, 0, $lenPattern));
    111         $ret = [$retRoute, $params];
    112 
    113         return $ret;
     125                if ($pathArray[$i] !== $routePatternArray[$i]) {
     126                    return null;
     127                }
     128            }
     129        }
     130
     131        return $params;
    114132    }
    115133
     
    145163        return $this->when($route, 'DELETE');
    146164    }
    147 
    148     // public function whenAny(): self
    149     // {
    150         // return $this->when('*');
    151     // }
    152 
    153     // public function whenNot(string $route): self
    154     // {
    155         // return $this->when('!' . $route);
    156     // }
    157165
    158166/**
     
    166174        $k = $this->tempRoute;
    167175        $method = $this->tempMethod;
    168         $this->handler[$k]['ALL'] = $func;
    169 
    170         return $this;
    171     }
    172 
    173     public function middleware($func, int $order = 50): self
     176        $this->handler[$k][$method] = $func;
     177
     178        return $this;
     179    }
     180
     181    // lightweight middleware, included in common middleware, as well as retrieved explicitely by getProtectChain
     182    public function protect($func, int $order = 50): self
     183    {
     184        return $this->middleware($func, $order, 'protect');
     185    }
     186
     187    public function middleware($func, int $order = 50, string $type = 'all'): self
    174188    {
    175189        if (($order < 9) or ($order > 99)) {
     
    180194
    181195        $k = $this->tempRoute;
    182         if (!isset($this->middleware[$k])) {
    183             $this->middleware[$k] = [];
    184         }
    185         $this->middleware[$k][] = ['handler' => $func, 'order' => $order];
     196        $method = $this->tempMethod;
     197        if (!isset($this->middleware[$k][$method])) {
     198            $this->middleware[$k][$method] = [];
     199        }
     200        $this->middleware[$k][$method][] = ['handler' => $func, 'order' => $order, 'type' => $type];
    186201
    187202        return $this;
     
    233248
    234249        return $ret;
     250    }
     251
     252    // this will also include all parents
     253    public function findAllRouteStack(string $routePattern): array
     254    {
     255        $allRoutePatterns = $this->findAllRoutePatterns($routePattern);
     256
     257        $routeStack = [];
     258        foreach ($allRoutePatterns as $route_) {
     259            $routeArray = strlen($route_) ? explode($this->routeSeparator, $route_) : [];
     260            for ($i = 0; $i <= count($routeArray); $i++) {
     261                $parentRoute_ = join($this->routeSeparator, array_slice($routeArray, 0, $i));
     262                $routeStack[$parentRoute_] = $parentRoute_;
     263            }
     264        }
     265
     266        return $routeStack;
    235267    }
    236268
     
    241273    * @return array Example: [['route' => admin/user, 'handler' => $func]]
    242274    */
    243     public function getHandlerChain(string $routePattern, string $method): array
     275    public function getHandlerChain(string $routePattern, string $method, ?string $type = null): array
    244276    {
    245277        $ret = [];
     
    260292
    261293    // main handler
    262         foreach ($allRoutePatterns as $k) {
    263             $h = $this->findHandler($k, $method);
    264             if ($h) {
    265                 $ret[] = ['route' => $routePattern, 'handler' => $h, 'order' => 100];
    266                 break;
    267             }
    268         }
    269 
    270         if (!$ret) {
    271             return $ret;
     294        if (null === $type) {
     295            foreach ($allRoutePatterns as $k) {
     296                $h = $this->findHandler($k, $method);
     297                if ($h) {
     298                    $ret[] = ['route' => $routePattern, 'handler' => $h, 'order' => 100];
     299                    break;
     300                }
     301            }
     302
     303            if (!$ret) {
     304                return $ret;
     305            }
    272306        }
    273307
    274308    // for middleware check all parent routes
     309        $methods_ = ('ALL' === $method) ? [$method] : [$method, 'ALL'];
    275310        $retMiddleware = [];
    276311        foreach ($routeStack as $route_) {
     
    278313                continue;
    279314            }
    280             foreach ($this->middleware[$route_] as $h_) {
    281                 $ret[] = ['route' => $route_] + $h_;
     315
     316            foreach ($methods_ as $method_) {
     317                if (!isset($this->middleware[$route_][$method_])) {
     318                    continue;
     319                }
     320                foreach ($this->middleware[$route_][$method_] as $h_) {
     321                    if ((null !== $type) && ($type !== $h_['type'])) {
     322                        continue;
     323                    }
     324                    $ret[] = ['route' => $route_] + $h_;
     325                }
    282326            }
    283327        }
     
    288332    }
    289333
     334    /**
     335    *
     336    * Gets a list of protection handlers for the route
     337    *
     338    * @return array Example: [['route' => admin/user, 'handler' => $func]]
     339    */
     340    public function getProtectChain(string $routePattern, string $method): array
     341    {
     342        return $this->getHandlerChain($routePattern, $method, 'protect');
     343    }
     344
    290345    private function sortRoutes(array $a, array $b)
    291346    {
  • plain-event-calendar/trunk/vendor/plainware/http/src/Server.php

    r3394866 r3400983  
    44
    55use Plainware\Core\App;
     6use Plainware\Http\RequestFactory;
    67use Plainware\Http\Request;
    78use Plainware\Http\Redirect;
     
    1516    public $app = App::class;
    1617    public $router = Router::class;
     18    public $requestFactory = RequestFactory::class;
    1719    public $uri = Uri::class;
    1820    private string $prefix = '';
     
    2729    public function run()
    2830    {
    29         $request = $this->buildRequest();
     31        $this->uri->setParamPrefix($this->prefix);
     32
     33        $request = $this->requestFactory->fromGlobals($this->prefix);
    3034        $ret = $this->handle($request);
    3135        $this->app->end();
     
    3539    }
    3640
    37     private function buildRequest(): Request
    38     {
    39         $request = new Request($this->prefix);
    40 
    41     // determine and set slug
    42         $slugParam = $this->uri->getSlugParam();
    43         $slugValue = $request->getQueryParam($slugParam) ?? '';
    44         $slug = $this->uri->normalizeSlug($slugValue);
    45 
    46     // dont't need slug as param anymore
    47         $request = $request->withQueryParam($slugParam, null);
    48 
    49         $request = $request->withSlug($slug);
    50         while ($slug = $this->router->isShortcut($slug)) {
    51             $request = $request->makeTo($slug);
    52         }
    53 
    54         $this->uri->setParamPrefix($this->prefix);
    55 
    56         return $request;
     41// lightweight check, testing only protect middlewares
     42    public function can(Request $request): bool
     43    {
     44        $path = $request->getPath();
     45        $method = $request->getRequestMethod();
     46
     47        list($routePattern,) = $this->router->parsePath($path);
     48        if (null === $routePattern) {
     49            return false;
     50        }
     51
     52        $chain = $this->router->getProtectChain($routePattern, $method);
     53// _print_r($chain);
     54        if (!$chain) {
     55            return true;
     56        }
     57
     58    // just boolean provided?
     59        foreach ($chain as $h) {
     60            if (is_bool($h)) {
     61                return $h;
     62            }
     63        }
     64
     65        array_unshift($chain, fn() => true);
     66        $handler = $this->buildHandler($chain);
     67        $ret = call_user_func($handler, $request);
     68
     69        if (!is_bool($ret)) {
     70            $ret = true;
     71        }
     72
     73        return $ret;
    5774    }
    5875
    5976    public function handle(Request $request)
    6077    {
    61         $route = $request->getRoute();
     78        $path = $request->getPath();
    6279        $method = $request->getRequestMethod();
    6380
    64         list($routePattern, $routeParams) = $this->router->parseRoute($route);
    65         if (false === $routePattern) {
     81        list($routePattern, $routeParams) = $this->router->parsePath($path);
     82        if (null === $routePattern) {
    6683            return null;
    6784        }
    6885
    69         if ($routePattern !== $route) {
    70             foreach ($routeParams as $k => $v) {
    71                 $request = $request->withRouteParam($k, $v);
    72             }
     86        $request = $request->withRoutePattern($routePattern);
     87        foreach ($routeParams as $k => $v) {
     88            $request = $request->withRouteParam($k, $v);
    7389        }
    7490
     
    88104    {
    89105        $firstElement = array_shift($chain);
    90         $func = $firstElement['handler'];
     106        if (is_array($firstElement) && isset($firstElement['handler'])){
     107            $func = $firstElement['handler'];
     108        } else {
     109            $func = $firstElement;
     110        }
    91111        $func = $this->factory($func);
    92112
    93113        foreach ($chain as $chainElement) {
    94             $decorator = $chainElement['handler'];
     114            if (is_array($chainElement) && isset($chainElement['handler'])){
     115                $decorator = $chainElement['handler'];
     116            } else {
     117                $decorator = $chainElement;
     118            }
    95119            $decorator = $this->factory($decorator);
    96120            $func = function (Request $request) use ($decorator, $func) {
     
    104128    public function prepareResponse($ret, Request $request): Response
    105129    {
     130        if (false === $ret) {
     131            $resp = new Response(403);
     132            $resp = $resp
     133                ->withBody('__This action is not allowed.__')
     134                ->withHeader('content-type', 'text/html')
     135                ;
     136
     137            return $resp;
     138        }
     139
    106140        if ($ret instanceof Response) {
    107141            $body = $ret->getBody();
     
    115149
    116150        if ($ret instanceof Redirect) {
    117             $slug = $ret->getSlug();
     151            $path = $ret->getPath();
    118152            $p = $ret->getParams();
    119153
    120             $request2 = $request->makeTo($slug, $p);
    121             $p = $request2->getQueryParams();
    122 
    123             $slug = $request2->getSlug();
    124             while ($shortcutSlug = $this->router->isShortcut($slug)) {
     154            $request_ = $this->requestFactory->from($request, $path, $p);
     155            $p = $request_->getQueryParams();
     156
     157            $path = $request_->getPath();
     158            while ($shortcut = $this->router->isShortcut($path)) {
     159                $path = $shortcut;
     160                continue;
     161               
    125162            // hack to pass layout param, will invent something else later
    126163                $layout = $p['layout-'] ?? null;
    127164
    128                 $request2 = $request2->makeTo($shortcutSlug);
    129                 $slug = $request2->getSlug();
    130 
    131                 $p = $request2->getQueryParams();
     165                $request_ = $this->requestFactory->from($request_, $shortcut);
     166                $path = $request_->getPath();
     167
     168                $p = $request_->getQueryParams();
    132169                if (null !== $layout) {
    133170                    $p['layout-'] = $layout;
     
    135172            }
    136173
    137             if ($messageId = $ret->getMessage()) {
    138                 $slug0 = $request->getSlug();
    139                 $slugString0 = $this->uri->slugToString($slug0);
    140                 $p['msg-'] = (1 == $messageId) ? $slugString0 : $slugString0 . '--' . $messageId;
    141             }
    142 
    143             $to = $this->uri->to($slug, $p);
     174            $to = $this->uri->to($path, $p);
    144175// echo '<br>';
    145176// echo $to;
  • plain-event-calendar/trunk/vendor/plainware/http/src/Uri.php

    r3394866 r3400983  
    88{
    99    public $app = App::class;
    10     private $slugParam = 'a';
     10    private $pathParam = '_';
    1111    private $paramPrefix = '';
    12     private $slugSeparator = '-';
     12    private $pathSeparator = '-';
    1313    private $passParamPrefix = '-';
    1414    private $hrefFunc = null;
     
    2727    }
    2828
    29     public function getSlugParam(): string
    30     {
    31         return $this->slugParam;
     29    public function getPathParam(): string
     30    {
     31        return $this->pathParam;
    3232    }
    3333
     
    3737    }
    3838
    39     public function normalizeSlug(string $slugValue): string
    40     {
    41         if ('/' !== $this->slugSeparator) {
    42             $slugValue = str_replace($this->slugSeparator, '/', $slugValue);
    43         }
    44 
    45         return $slugValue;
    46     }
    47 
    48     public function to(string $slug, array $params = [], string $context = ''): string
    49     {
    50         $slugParam = $this->getSlugParam();
    51 
    52         if ('/' !== $this->slugSeparator) {
    53             $slugValue = str_replace('/', $this->slugSeparator, $slug);
     39    public function normalizePath(string $path): string
     40    {
     41        if ('/' !== $this->pathSeparator) {
     42            $path = str_replace($this->pathSeparator, '/', $path);
     43        }
     44
     45        return $path;
     46    }
     47
     48    public function to(string $path, array $params = [], string $context = ''): string
     49    {
     50        $pathParam = $this->getPathParam();
     51
     52        if ('/' !== $this->pathSeparator) {
     53            $pathValue = str_replace('/', $this->pathSeparator, $path);
    5454        } else {
    55             $slugValue = $slug;
    56         }
    57         $params[$slugParam] = $slugValue;
     55            $pathValue = $path;
     56        }
     57        $params[$pathParam] = $pathValue;
    5858
    5959        $ks = array_keys($params);
     
    7777    }
    7878
    79     public function toFrontPage(array $slug, array $params = []): string
    80     {
    81     }
    82 
    8379    public function asset(string $to): string
    8480    {
  • plain-event-calendar/trunk/vendor/plainware/rest/src/Ui/Renderer/OpenApiInfoRenderer.php

    r3394866 r3400983  
    99use Plainware\Ui\Component\InlineList;
    1010use Plainware\Ui\Component\HtmlElement;
    11 use Plainware\Rest\Service\YamlParser;
     11use Plainware\Yaml\YamlParser;
    1212use Plainware\Rest\Ui\Component\OpenApiInfoComponent;
    1313
  • plain-event-calendar/trunk/vendor/plainware/ui-ajax/assets/ajax.js

    r3372822 r3400983  
    294294
    295295        // var fetchCount = 0;
    296         var slugParamName = $container.getAttribute('data-pw-slug-param-name');
    297         if (null === slugParamName) {
    298             slugParamName = 'p';
     296        var pathParamName = $container.getAttribute('data-pw-path-param-name');
     297        if (null === pathParamName) {
     298            pathParamName = 'p';
    299299        }
    300300
     
    380380            var href = a.href;
    381381
    382         // contains our slug
    383             if (!uriContainsParameter(href, slugParamName)) {
     382        // contains our path
     383            if (!uriContainsParameter(href, pathParamName)) {
    384384                return;
    385385            }
     
    406406            // find target element
    407407                var $thisContainer;
    408                 var targetSlug = getUriParameter(href, slugParamName);
     408                var targetPath = getUriParameter(href, pathParamName);
    409409
    410410                if (!$thisContainer) {
     
    568568            }
    569569
    570         // contains our slug
    571             if (!uriContainsParameter(href, slugParamName)) {
     570        // contains our path
     571            if (!uriContainsParameter(href, pathParamName)) {
    572572                return;
    573573            }
  • plain-event-calendar/trunk/vendor/plainware/ui-ajax/src/Renderer/MainRendererDecorator.php

    r3394866 r3400983  
    1414    public function __invoke(Main $el, Request $request, callable $next)
    1515    {
    16         if ($el->getLayoutId()) {
     16        if ($request->getAttribute('layoutId')) {
    1717            return ($next)($el, $request);
    1818        }
    1919
    2020        $el->withJs('vendor/plainware/ui-ajax/assets/ajax.js');
     21        $ret = ($next)($el, $request);
    2122
    22         $slugParam = $this->uri->getParamPrefix() . $this->uri->getSlugParam();
     23        $pathParam = $this->uri->getParamPrefix() . $this->uri->getPathParam();
    2324        $layoutParam = $this->uri->getParamPrefix() . 'layout-';
    2425
    2526        $wrapper = (new HtmlElement('div'))
    2627            ->withAttr('class', 'pw-ajax-container')
    27             ->withAttr('data-pw-slug-param-name', $slugParam)
     28            ->withAttr('data-pw-path-param-name', $pathParam)
    2829            ->withAttr('data-pw-layout-param-name', $layoutParam)
    2930            ;
    30         $el->wrapBy($wrapper);
     31        $ret = $wrapper->append($ret);
    3132
    32         return ($next)($el, $request);
     33        return $ret;
    3334    }
    3435}
  • plain-event-calendar/trunk/vendor/plainware/ui-wordpress/boot.php

    r3394866 r3400983  
    2424        if (is_admin()) {
    2525            $ui
    26                 ->renderer(Nav::class, Renderer\AdminNavRenderer::class)
    2726                ->renderer(Menu::class, Renderer\AdminMenuRenderer::class)
    2827                ->renderer(Table::class, Renderer\AdminTableRenderer::class)
  • plain-event-calendar/trunk/vendor/plainware/ui-wordpress/src/Renderer/AdminMainMenuRenderer.php

    r3394866 r3400983  
    66use Plainware\Core\Translate;
    77use Plainware\Http\Uri;
     8use Plainware\Http\RequestFactory;
    89use Plainware\Ui\Ui;
    910use Plainware\Ui\HtmlHelper;
     11use Plainware\Ui\Component\Link;
    1012use Plainware\Ui\Component\MainMenu;
    1113use Plainware\Ui\Component\TabMenu;
    12 use Plainware\Ui\Renderer\TabMenuRenderer;
    13 use Plainware\Ui\Component\Link;
     14use Plainware\Ui\Renderer\MenuRenderer;
    1415
    1516class AdminMainMenuRenderer
     
    1819    public $uri = Uri::class;
    1920    public Translate $translate;
    20     public TabMenuRenderer $tabMenuRenderer;
     21    public $menuRenderer = MenuRenderer::class;
     22    public $requestFactory = RequestFactory::class;
    2123    private $wpAdminPageSlug = 'sh5';
    2224
     
    3133
    3234        $items = $el->getItems();
     35        $items = array_filter($items, fn($e) => $this->menuRenderer->isItemAllowed($e, $request));
    3336        if (!$items) {
    3437            return $ret;
    3538        }
    3639
    37         $currentSlug = $el->getCurrentSlug() ?? '';
    38         $currentSlugArray = strlen($currentSlug) ? explode('/', $currentSlug) : [];
     40        $currentPath = $el->getCurrentPath() ?? '';
     41        $currentPathArray = strlen($currentPath) ? explode('/', $currentPath) : [];
    3942
    4043        $items = $el->getItems();
     
    4750
    4851            if ($item instanceof Link) {
    49                 $request_ = $request->makeTo($item->getSlug(), $item->getParams());
    50                 $thisSlug = $request_->getSlug();
    51                 $thisSlugArray = strlen($thisSlug) ? explode('/', $thisSlug) : [];
     52                $path_ = $request->resolvePath($item->getPath());
     53                $pathArray_ = strlen($path_) ? explode('/', $path_) : [];
    5254
    53                 $len = min(count($thisSlugArray), count($currentSlugArray));
     55                $len = min(count($pathArray_), count($currentPathArray));
    5456                $isOn = true;
    5557                for ($i_ = 0; $i_ < $len; $i_++) {
    56                     if ($thisSlugArray[$i_] !== $currentSlugArray[$i_]) {
     58                    if ($pathArray_[$i_] !== $currentPathArray[$i_]) {
    5759                        $isOn = false;
    5860                        break;
     
    6365                }
    6466            }
    65 
    6667            $i++;
    6768        }
     
    7374
    7475        return $tabMenu;
    75 
    76         _print_r($items);
    77 
    78         $ret = '';
    79         $ret = ($this->tabMenuRenderer)($el);
    80         // $ret = ($next)($ret);
    81         return $ret;
    82 
    83         // $items = $el->getItems();
    84         // if (!$items) {
    85             // return $ret;
    86         // }
    8776
    8877        $this->setupWordpressAdminSubmenu($el);
     
    111100            $menuTitle = strip_tags($label);
    112101
    113             $href = $this->uri->to($item->getSlug(), $item->getParams());
     102            $href = $this->uri->to($item->getRoute(), $item->getParams());
    114103            remove_submenu_page($mainMenuSlug, $href);
    115104
  • plain-event-calendar/trunk/vendor/plainware/ui-wordpress/src/Renderer/AdminTabMenuRenderer.php

    r3394866 r3400983  
    99use Plainware\Ui\Component\TabMenu;
    1010use Plainware\Ui\Component\Link;
     11use Plainware\Ui\Renderer\MenuRenderer;
    1112
    1213class AdminTabMenuRenderer
     
    1415    public $ui = Ui::class;
    1516    public $uri = Uri::class;
     17    public $menuRenderer = MenuRenderer::class;
    1618
    1719    public function __invoke(TabMenu $el, Request $request)
    1820    {
    1921        $items = $el->getItems();
     22        $items = array_filter($items, fn($e) => $this->menuRenderer->isItemAllowed($e, $request));
    2023
    2124        if (!$items) {
  • plain-event-calendar/trunk/vendor/plainware/ui-wordpress/src/Renderer/MainRendererDecorator.php

    r3394866 r3400983  
    77use Plainware\Ui\Ui;
    88use Plainware\Ui\Component\Main;
     9use Plainware\Ui\Component\HtmlElement;
    910
    1011final class MainRendererDecorator
     
    1819        $nextRet = ($next)($el, $request);
    1920
    20         if ($el->getLayoutId()) {
     21        if ($request->getAttribute('layoutId')) {
    2122            return $nextRet;
    2223        }
     
    5657        }
    5758
    58         $ret = [];
    59 
    60         $ret[] = '<div class="wrap">';
    61         $ret[] = $this->ui->render($nextRet, $request);
    62         $ret[] = '</div>';
    63 
    64         $ret = join('', $ret);
     59        $wrapper = (new HtmlElement('div'))
     60            ->withAttr('class', 'wrap')
     61            ;
     62        $ret = $wrapper->append($nextRet);
    6563
    6664        return $ret;
  • plain-event-calendar/trunk/vendor/plainware/ui/assets/core.css

    r3394866 r3400983  
    425425#pw2 table.pw-inline-table, #pw2 table.pw-inline-table th, #pw2 table.pw-inline-table td {width: auto;}
    426426
    427 #pw2 .pw-header-user {margin: 0 0 .5em 0; padding: 0 0 .25em 0; border-bottom: var(--border-color) 1px solid;}
    428 #pw2 .pw-header-mainmenu {margin: 0 0 .5em 0;}
    429 #pw2 .pw-header-tabmenu {margin: 0 0 1em 0;}
    430 #pw2 .pw-header-breadcrumbs {margin: 0 0 .25em 0; font-size: .9em; padding: 0 0;}
    431 #pw2 .pw-header-title {margin: 0 0 1em 0;}
    432 #pw2 .pw-header-flashmessage {margin: 0 0 1em 0;}
    433 #pw2 .pw-footer-pagemenu {margin: .5em 0 0 0;}
     427/* #pw2 .pw-header-user {margin: 0 0 .5em 0; padding: 0 0 .25em 0; border-bottom: var(--border-color) 1px solid;} */
     428/* #pw2 .pw-header-mainmenu {margin: 0 0 .5em 0;} */
     429/* #pw2 .pw-header-tabmenu {margin: 0 0 1em 0;} */
     430/* #pw2 .pw-header-breadcrumbs {margin: 0 0 .25em 0; font-size: .9em; padding: 0 0;} */
     431/* #pw2 .pw-header-title {margin: 0 0 1em 0;} */
     432/* #pw2 .pw-header-flashmessage {margin: 0 0 1em 0;} */
     433/* #pw2 .pw-footer-pagemenu {margin: .5em 0 0 0;} */
    434434
    435435#pw2 label[for] {display: block; width: 100%; padding: 0; margin: 0;}
  • plain-event-calendar/trunk/vendor/plainware/ui/boot.php

    r3394866 r3400983  
    1212    $app->onBoot(Router::class, function ($router) {
    1313        $router
    14             // ->whenAny()
    15                 // ->middleware(Middleware\UiMiddleware::class, 85)
    16                 // ->middleware(Middleware\ExceptionMiddleware::class, 14)
    17                 // ->middleware(Middleware\MainFlashMessageMiddleware::class, 90)
    18 
    1914            ->when('')
    2015                ->middleware(Middleware\UiMiddleware::class, 85)
    2116                ->middleware(Middleware\ExceptionMiddleware::class, 14)
    22                 // ->middleware(Middleware\MainFlashMessageMiddleware::class, 90)
     17                ->middleware(Middleware\FlashMessageMiddleware::class, 90)
    2318                ->middleware(Middleware\PageLayoutMiddleware::class, 90)
    24 
    25             // ->whenNot('install')
    26                 // ->middleware(Middleware\PageLayoutMiddleware::class, 90)
    2719            ;
    2820    });
     
    3224            ->renderer(Component\AbstractTag::class, Renderer\HtmlElementRenderer::class)
    3325            ->renderer(Component\Main::class, Renderer\MainRenderer::class)
    34             ->renderer(Component\Main::class, Renderer\PrintViewMainRendererDecorator::class)
     26            // ->renderer(Component\Main::class, Renderer\PrintViewMainRendererDecorator::class)
    3527            ->renderer(Component\Link::class, Renderer\LinkRenderer::class)
    3628            ->renderer(Component\Details::class, Renderer\DetailsRenderer::class)
     
    4335            ->renderer(Component\FlashMessage::class, Renderer\FlashMessageRenderer::class)
    4436            ->renderer(Component\Pager::class, Renderer\PagerRenderer::class)
    45             ->renderer(Component\Nav::class, Renderer\NavRenderer::class)
    4637            ->renderer(Component\Menu::class, Renderer\MenuRenderer::class)
    4738            ->renderer(Component\InlineList::class, Renderer\InlineListRenderer::class)
    48             ->renderer(Component\BlockList::class, Renderer\BlockListRenderer::class)
    4939            ->renderer(Component\TabMenu::class, Renderer\TabMenuRenderer::class)
    5040            ->renderer(Component\Grid::class, Renderer\GridRenderer::class)
    5141            ->renderer(Component\MainMenu::class, Renderer\MainMenuRenderer::class)
     42            ->renderer(Component\Partial::class, Renderer\PartialRenderer::class)
    5243            ->renderer(Component\TabbedMenu::class, Renderer\TabbedMenuRenderer::class)
     44            ->renderer(Component\SpacedContainer::class, Renderer\SpacedContainerRenderer::class)
    5345            ->renderer(Component\Form::class, Renderer\FormRenderer::class)
    5446            ->renderer(Component\Form\Button::class, Renderer\Form\ButtonRenderer::class)
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Component/Form/TimeInput.php

    r3332867 r3400983  
    99class TimeInput extends AbstractInput implements InputInterface
    1010{
    11     private int $step = 5;
     11    private int $step = 1;
    1212
    1313    public function grab(Request $request)
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Component/Link.php

    r3394866 r3400983  
    55final class Link extends AbstractTag
    66{
    7     private ?string $slug = null;
     7    private ?string $path = null;
    88    private array $params = [];
    99    private $label;
     
    1515    private string $fullUri = '';
    1616
    17     public function __construct(?string $slug = null, array $params = [])
     17    public function __construct(?string $path = null, array $params = [])
    1818    {
    19         if (null !== $slug) {
    20             $this->setSlug($slug);
    21         }
     19        $this->path = $path;
    2220        $this->params = $params;
    2321    }
     
    3533    }
    3634
    37     public function getSlug(): string
     35    public function getPath(): string
    3836    {
    39         return $this->slug;
     37        return $this->path;
    4038    }
    4139
    42     public function setSlug(string $slug): self
    43     {
    44         $this->slug = $slug;
     40    // public function setSlug(string $slug): self
     41    // {
     42        // $this->slug = $slug;
    4543
    46         return $this;
    47     }
     44        // return $this;
     45    // }
    4846
    4947    public function getParams(): array
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Component/Main.php

    r3372822 r3400983  
    33namespace Plainware\Ui\Component;
    44
    5 use Plainware\Ui\Component\Menu;
    6 
    7 final class Main extends Container
     5final class Main
    86{
    9     private $title = '';
    10     private Menu $nav;
     7    private $content = '';
    118    private array $css = [];
    129    private array $js = [];
    13     private string $layoutId = '';
    1410    private array $messages = [];
    1511    private array $errorMessages = [];
    1612
    17     public function __construct($content = null)
     13    public function __construct($content = '')
    1814    {
    19         if (null !== $content) {
    20             $this->addWithId('main', 50, $content);
    21         }
    22         $this->nav = new Menu();
     15        $this->content = $content;
     16    }
     17
     18    public function getContent()
     19    {
     20        return $this->content;
     21    }
     22
     23    public function withContent($content): self
     24    {
     25        $ret = clone $this;
     26        $ret->content = $content;
     27
     28        return $ret;
    2329    }
    2430
     
    4753    }
    4854
    49     public function setLayoutId(string $id): self
    50     {
    51         $this->layoutId = $id;
    52 
    53         return $this;
    54     }
    55 
    56     public function getLayoutId(): string
    57     {
    58         return $this->layoutId;
    59     }
    60 
    6155    public function withCss($file): self
    6256    {
     
    8276        return $this->js;
    8377    }
    84 
    85     public function getTitle()
    86     {
    87         return $this->title;
    88     }
    89 
    90     public function setTitle($title): self
    91     {
    92         $this->title = $title;
    93 
    94         return $this;
    95     }
    96 
    97     public function getNav(): Menu
    98     {
    99         return $this->nav;
    100     }
    101 
    102     public function withNav(): Menu
    103     {
    104         return $this->getNav();
    105     }
    10678}
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Component/MainMenu.php

    r3372822 r3400983  
    77class MainMenu extends Menu
    88{
    9     private $currentSlug = null;
     9    private $currentPath = null;
    1010
    11     public function setCurrentSlug($currentSlug): self
     11    public function setCurrentPath($v): self
    1212    {
    13         $this->currentSlug = $currentSlug;
     13        $this->currentPath = $v;
    1414
    1515        return $this;
    1616    }
    1717
    18     public function getCurrentSlug()
     18    public function getCurrentPath()
    1919    {
    20         return $this->currentSlug;
     20        return $this->currentPath;
    2121    }
    2222}
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Component/Section.php

    r3372822 r3400983  
    44
    55use Plainware\Ui\Component\Menu;
    6 use Plainware\Ui\Component\Nav;
    76
    87final class Section
     
    1211    private $content = '';
    1312    private Menu $menu;
    14     // private Nav $nav;
    1513    private Menu $nav;
    1614    private array $attr = [];
     
    2321        }
    2422        $this->menu = new Menu();
    25         // $this->nav = new Nav();
    2623        $this->nav = new Menu();
    2724    }
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Middleware/ExceptionMiddleware.php

    r3394866 r3400983  
    55use Plainware\Http\Request;
    66use Plainware\Ui\Component\Main;
    7 use Plainware\Ui\Ui;
    87
    98class ExceptionMiddleware
    109{
    11     public $ui = Ui::class;
    12 
    1310    public function __invoke(Request $request, callable $next)
    1411    {
     
    1714        } catch (\Exception $e) {
    1815            $ret = $e->getMessage();
    19 
     16            $ret = '__Error__' . ': ' . $ret;
    2017            $ret = new Main($ret);
    21             $ret->setTitle('__Error__');
    22 
    23             // $ret = $this->ui->render($ret, $request);
    2418        }
    2519
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Middleware/PageLayoutMiddleware.php

    r3394866 r3400983  
    55use Plainware\Http\Request;
    66use Plainware\Http\Router;
     7use Plainware\Http\RequestFactory;
     8use Plainware\Http\Server;
    79use Plainware\Core\App;
    810use Plainware\Ui\Ui;
    911use Plainware\Ui\PageMap;
     12use Plainware\Ui\Component\SpacedContainer;
    1013use Plainware\Ui\Component\Link;
    1114use Plainware\Ui\Component\Section;
     
    1518use Plainware\Ui\Component\TabMenu;
    1619use Plainware\Ui\Component\HtmlElement;
     20use Plainware\Ui\Component\InlineList;
    1721use Plainware\Ui\Component\Breadcrumbs;
     22use Plainware\Ui\Component\FlashMessage;
    1823
    1924final class PageLayoutMiddleware
     
    2126    public $app = App::class;
    2227    public $router = Router::class;
     28    public $server = Server::class;
    2329    public $pageMap = PageMap::class;
     30    public $requestFactory = RequestFactory::class;
    2431    public $ui = Ui::class;
    2532
     
    3138        }
    3239
    33         $route = $request->getRoute();
    34 
    35     // all route stack starting from absolute root
    36         $routeArray = strlen($route) ? explode('/', $route) : [];
    37         $routeStack = [];
    38         for ($i = 0; $i <= count($routeArray); $i++) {
    39             $route_ = join('/', array_slice($routeArray, 0, $i));
    40             list($routePattern_, $routeParams_) = $this->router->parseRoute($route_);
    41             $routeStack[$route_] = [$routePattern_, $routeParams_];
    42         }
    43 
    44     // TABS find nearest tab root if any. If found, then redefine title and render tabs
    45 // _print_r($routeStack);
    46         $tabRouteRoot = null;
     40        $path = $request->getPath();
     41        if (in_array($path, ['install'])) {
     42            return $ret;
     43        }
     44
     45        $sections = $this->buildSections($request);
     46        $content = $ret->getContent();
     47        if ($content) {
     48            $sections[] = [PageMap::POS_CONTENT, $ret->getContent()];
     49        }
     50
     51    // FLASH MESSAGE
     52        $errorMessages = $ret->getErrorMessages();
     53        $messages = $ret->getMessages();
     54
     55        if ($errorMessages or $messages) {
     56            $v = new SpacedContainer();
     57            foreach ($errorMessages as $msg) {
     58                $msg = (new FlashMessage($msg))->setError();
     59                $v->add($msg);
     60            }
     61            foreach ($messages as $msg) {
     62                $msg = new FlashMessage($msg);
     63                $v->add($msg);
     64            }
     65
     66            $v = (new HtmlElement('div'))
     67                ->append($v)
     68                ->withAttr('class', 'pw-header-flashmessage')
     69                ;
     70
     71            $sections[] = [PageMap::POS_FLASHMESSAGE, $v];
     72        }
     73
     74// _print_r($sections);
     75// exit;
     76
     77        usort($sections, fn($a, $b) => $a[0] - $b[0]);
     78        $sections = array_column($sections, 1);
     79
     80        $content = new SpacedContainer();
     81        foreach ($sections as $k => $v) {
     82            $content->add($v);
     83        }
     84
     85        $ret = $ret->withContent($content);
     86
     87        return $ret;
     88    }
     89
     90    private function buildSections(Request $request): array
     91    {
     92        $ret = [];
     93
     94    // this path
     95        $path = $request->getPath();
     96
     97    // all path stack starting from absolute root
     98        $pathArray = strlen($path) ? explode('/', $path) : [];
     99        $pathStack = [];
     100        for ($i = 0; $i <= count($pathArray); $i++) {
     101            $path_ = join('/', array_slice($pathArray, 0, $i));
     102            $pathStack[] = $path_;
     103        }
     104
     105    // MAIN MENU
     106        $mainRoot = $mainRootRequest = null;
     107        foreach (array_reverse($pathStack) as $path_) {
     108            $isRoot = (
     109                (!strlen($path_))
     110                or $this->pageMap->isRoot($path_)
     111                // or $this->pageMap->isRoot($route_)
     112                ) ? true : false;
     113            if (!$isRoot) {
     114                continue;
     115            }
     116
     117            $mainRoot = $path_;
     118            $mainRootRequest = $this->requestFactory->from($request, $mainRoot);
     119            break;
     120        }
     121
     122    // BUILD: MAINMENU
     123        // $ret[PageMap::POS_MAINMENU] = 'mainmenu';
     124        if ($mainRoot === $path) {
     125            $items_ = [];
     126        } else {
     127            $items_ = $this->pageMap->getMenuItems($mainRoot);
     128            $items_ = array_merge(...array_values($items_));
     129        }
     130        if ($items_) {
     131            $ret_ = new MainMenu();
     132            foreach ($items_ as $item) {
     133                $ret_->add(50, $item);
     134            }
     135            $ret_->setCurrentPath($path);
     136            $ret_ = $this->ui->render($ret_, $mainRootRequest);
     137            if (strlen($ret_)) {
     138                $ret_ = (new HtmlElement('div'))
     139                    ->append($ret_)
     140                    ->withAttr('class', 'pw-header-mainmenu')
     141                    ;
     142                $ret[] = [PageMap::POS_MAINMENU, $ret_];
     143            }
     144        }
     145
     146    // TABS find nearest tab root if any
     147        $tabRoot = $tabRootRequest = null;
    47148        $j = 0;
    48         foreach (array_reverse($routeStack) as $route_ => list($routePattern_, $routeParams_)) {
     149        foreach (array_reverse($pathStack) as $path_) {
    49150            $j++;
    50151        // check only the page and the nearest parent
     
    53154            }
    54155
    55             $isTabRoot = $this->pageMap->isTabRoot($routePattern_);
     156            $isTabRoot = $this->pageMap->isTabRoot($path_);
    56157            if (!$isTabRoot) {
    57158                continue;
    58159            }
    59160
    60 // echo "'$route_' IST ROOT<br>";
    61 // exit;
    62 
    63             $request_ = $request->makeTo($route_);
    64             foreach ($routeParams_ as $k => $v) {
    65                 $request_ = $request_->withRouteParam($k, $v);
    66             }
    67 
    68         // tab root title
    69             $tabRootTitle = $this->pageMap->getTabRootTitle($routePattern_);
    70             $tabRootTitle = $this->ui->render($tabRootTitle, $request_);
    71             $ret->setTitle($tabRootTitle);
    72 
    73         // tabs
    74             $tabItems = $this->pageMap->getTabItems($routePattern_);
    75 
    76             $tabMenu = new TabMenu();
    77             $ii = 1;
    78             foreach ($tabItems as $item) {
    79                 $itemId = $ii++;
    80                 $tabMenu->add($itemId, $item);
    81                 if ($item instanceof Link) {
    82                     $route2_ = $request->resolveRoute($item->getSlug(), $route_);
    83                     if ($route2_ == $route) {
    84                         $tabMenu->setCurrent($itemId);
     161            $tabRoot = $path_;
     162            $tabRootRequest = $this->requestFactory->from($request, $tabRoot);
     163
     164        // check tab items, if have no current then it's not the tab root
     165            $tabItems = $this->pageMap->getTabItems($tabRoot);
     166            $isReally_ = true;
     167            if ($tabRoot !== $path) {
     168                $isReally_ = false;
     169                foreach ($tabItems as $item_) {
     170                    if ($item_ instanceof Link) {
     171                        $path_ = $tabRootRequest->resolvePath($item_->getPath());
     172                        if ($path_ == $path) {
     173                            $isReally_ = true;
     174                            break;
     175                        }
    85176                    }
    86177                }
    87178            }
    88 
    89             $tabMenu = $this->ui->render($tabMenu, $request_);
    90             if (strlen($tabMenu)) {
    91                 $ret->add(27, $tabMenu);
    92                 $tabRouteRoot = $route_;
    93             }
    94 
     179            if (!$isReally_) {
     180                $tabRoot = null;
     181            }
    95182            break;
    96183        }
    97184
    98     // MAIN MENU
    99         $mainRootRoute = '';
    100 // _print_r($routeStack);
    101         foreach (array_reverse($routeStack) as $route_ => list($parsedRoute_, $routeParams_)) {
    102             $isRoot = ((!strlen($route_)) or $this->pageMap->isRoot($parsedRoute_)) ? true : false;
    103             if (!$isRoot) {
     185    // BREADCRUMBS FROM ROOT TO TOP (IF ROOT ISN'T TOP)
     186        $breadcrumbsFromRootToTop = [];
     187    // if root is not index then show breadcrumbs to index too
     188    // upd: don't need
     189        if (false) {
     190            if ('' !== $mainRoot) {
     191                foreach ($routeStack as $route_ => list($parsedRoute_, $routeParams_)) {
     192                    $breadcrumbsFromRootToTop[] = $route_;
     193                    if ($route_ === $mainRoot) {
     194                        break;
     195                    }
     196                }
     197            }
     198        }
     199
     200    // BREADCRUMBS TO ROOT
     201        $breadcrumbsToRoot = [];
     202        foreach ($pathStack as $path_) {
     203            if ($path_ === $tabRoot) {
     204                break;
     205            }
     206            if ($path_ === $path) {
     207                break;
     208            }
     209            if (strlen($mainRoot) && (strlen($path_) <= strlen($mainRoot))) {
    104210                continue;
    105211            }
    106 
    107             $mainRootRoute = $route_;
    108 
    109             $rootMenuItems = $this->pageMap->getMenuItems($parsedRoute_);
    110             if (!$rootMenuItems) {
    111                 break;
    112             }
    113 
    114             $rootMenu = new MainMenu();
    115             foreach ($rootMenuItems as $item) {
    116                 $rootMenu->add(50, $item);
    117             }
    118             $rootMenu->setCurrentSlug($route);
    119 
    120             $request_ = $request->makeTo($route_);
    121             foreach ($routeParams_ as $k => $v) {
    122                 $request_ = $request_->withRouteParam($k, $v);
    123             }
    124 
    125             $rootMenu = $this->ui->render($rootMenu, $request_);
    126             if (strlen($rootMenu)) {
    127                 $rootMenu = (new HtmlElement('div'))
    128                     ->append($rootMenu)
    129                     ->withAttr('class', 'pw-header-mainmenu')
    130                     ;
    131                 $ret->add(22, $rootMenu);
    132             }
    133 
    134             break;
    135         }
    136 
    137     // BREADCRUMBS TO ROOT
    138         $breadcrumbLinks = [];
    139         foreach ($routeStack as $route_ => list($parsedRoute_, $routeParams_)) {
    140             if ($route_ === $tabRouteRoot) {
    141                 break;
    142             }
    143             if ($route_ === $route) {
    144                 break;
    145             }
    146             if (strlen($mainRootRoute) && (strlen($route_) <= strlen($mainRootRoute))) {
    147                 continue;
    148             }
    149 
    150             $title_ = $this->pageMap->getBreadcrumbTitle($parsedRoute_);
    151             if ($title_) {
    152                 $link = (new Link($route_))->setLabel($title_);
    153                 $breadcrumbLinks[] = $link;
    154             }
    155         }
    156         if ($breadcrumbLinks) {
    157             $breadcrumbs = new Breadcrumbs($breadcrumbLinks);
    158             $breadcrumbs = $this->ui->render($breadcrumbs, $request);
    159             if (strlen($breadcrumbs)) {
    160                 $breadcrumbs = (new HtmlElement('div'))
    161                     ->append($breadcrumbs)
    162                     ->withAttr('class', 'pw-header-breadcrumbs')
    163                     ;
    164                 $ret->add(25, $breadcrumbs);
    165             }
    166         }
    167 
    168     // BREADCRUMBS TO INDEX
    169     // if root is not index then show breadcrumbs to index too
    170         if ('' !== $mainRootRoute) {
    171             $breadcrumbLinks = [];
    172             foreach ($routeStack as $route_ => list($parsedRoute_, $routeParams_)) {
    173                 $title_ = $this->pageMap->getBreadcrumbTitle($parsedRoute_);
     212            $breadcrumbsToRoot[] = $path_;
     213        }
     214
     215    // BUILD: TABS
     216        if (null !== $tabRoot) {
     217            // $ret[PageMap::POS_TABS] = 'tabs';
     218            $items_ = $tabItems;
     219            if ($items_) {
     220                $ret_ = new TabMenu();
     221                $ii = 1;
     222                foreach ($items_ as $item) {
     223                    $itemId = $ii++;
     224                    $ret_->add($itemId, $item);
     225                    if ($item instanceof Link) {
     226                        $path_ = $tabRootRequest->resolvePath($item->getPath());
     227                        if ($path_ == $path) {
     228                            $ret_->setCurrent($itemId);
     229                        }
     230                    }
     231                }
     232                $ret_ = $this->ui->render($ret_, $tabRootRequest);
     233                if (strlen($ret_)) {
     234                    $ret[] = [PageMap::POS_TABS, $ret_];
     235                }
     236            }
     237        }
     238
     239    // BUILD: BREADCRUMBS TO ROOT
     240        if ($breadcrumbsToRoot) {
     241            // $ret[PageMap::POS_BREADCRUMBS_TO_ROOT] = 'breadcrumbs-to-root';
     242            $items_ = [];
     243            foreach ($breadcrumbsToRoot as $path_) {
     244                $title_ = $this->pageMap->getBreadcrumbTitle($path_);
     245                if ($title_) {
     246                    $link = (new Link($path_))->setLabel($title_);
     247                    $items_[] = $link;
     248                }
     249            }
     250            if ($items_) {
     251                $ret_ = new Breadcrumbs($items_);
     252                $ret_ = $this->ui->render($ret_, $request);
     253                if (strlen($ret_)) {
     254                    $ret_ = (new HtmlElement('div'))
     255                        ->append($ret_)
     256                        ->withAttr('class', 'pw-header-breadcrumbs')
     257                        ;
     258                    $ret[] = [PageMap::POS_BREADCRUMBS, $ret_];
     259                }
     260            }
     261        }
     262
     263    // BUILD: BREADCRUMBS FROM ROOT TO TOP
     264        if ($breadcrumbsFromRootToTop) {
     265            // $ret[PageMap::POS_BREADCRUMBS_ROOT_TO_TOP] = 'breadcrumbs-root-to-top';
     266            $items_ = [];
     267            foreach ($breadcrumbsFromRootToTop as $route_) {
     268                list($routePattern_, $routeParams_) = $routeStack[$route_];
     269                $title_ = $this->pageMap->getBreadcrumbTitle($routePattern_);
    174270                if ($title_) {
    175271                    $link = (new Link($route_))->setLabel($title_);
    176                     $breadcrumbLinks[] = $link;
    177                 }
    178                 if ($route_ === $mainRootRoute) {
    179                     break;
    180                 }
    181             }
    182 
    183             if ($breadcrumbLinks) {
    184                 $breadcrumbs = new Breadcrumbs($breadcrumbLinks);
    185                 $breadcrumbs = $this->ui->render($breadcrumbs, $request);
    186                 if (strlen($breadcrumbs)) {
    187                     $breadcrumbs = (new HtmlElement('div'))
    188                         ->append($breadcrumbs)
     272                    $items_[] = $link;
     273                }
     274            }
     275            if ($items_) {
     276                $ret_ = new Breadcrumbs($items_);
     277                $ret_ = $this->ui->render($ret_, $request);
     278                if (strlen($ret_)) {
     279                    $ret_ = (new HtmlElement('div'))
     280                        ->append($ret_)
    189281                        ->withAttr('class', 'pw-header-breadcrumbs')
    190282                        ;
    191                     $ret->add(21, $breadcrumbs);
    192                 }
    193             }
    194         }
    195 
    196     // NAV & MENU
    197         list($parsedRoute, $routeParams) = $routeStack[$route];
    198         $navItems = $this->pageMap->getNavItems($parsedRoute);
    199         $menuItems = $this->pageMap->getMenuItems($parsedRoute);
    200 
    201     // NAV: at the top of the page
    202         if ($navItems) {
    203             if (null !== $tabRouteRoot) {
    204                 $menu = new Menu();
    205                 foreach ($navItems as $item) {
    206                     $menu->add(50, $item);
    207                 }
    208                 $menu = $this->ui->render($menu, $request);
    209                 if (strlen($menu)) {
    210                     $ret->add(35, new Section($menu));
    211                 }
    212             } else {
    213                 foreach ($navItems as $item) {
    214                     $ret->withNav()->add(50, $item);
    215                 }
    216             }
    217         }
    218 
    219     // MENU: at the top of the page
    220         if ($menuItems) {
    221             $menu = new Menu();
    222             foreach ($menuItems as $item) {
    223                 $menu->add(50, $item);
    224             }
    225             $menu = $this->ui->render($menu, $request);
    226             if (strlen($menu)) {
    227                 $menu = (new HtmlElement('div'))
    228                     ->append($menu)
    229                     ->withAttr('class', 'pw-footer-pagemenu')
    230                     ;
    231                 $ret->add(75, $menu);
     283                    $ret[] = [PageMap::POS_BREADCRUMBS_ROOT_TO_TOP, $ret_];
     284                }
     285            }
     286        }
     287
     288    // BUILD: OTHER MENUS
     289        $hasTabRoot = ((null !== $tabRoot) && ($tabRoot !== $path)) ? true : false;
     290        $movPosIfTabs = [
     291            PageMap::POS_TITLE => PageMap::POS_SUBTITLE,
     292            PageMap::POS_MENU => PageMap::POS_SUBMENU,
     293        ];
     294
     295        $menus = [];
     296        $allItems_ = $this->pageMap->getMenuItems($path);
     297        foreach ($allItems_ as $pos_ => $items_) {
     298        // if there's tabroot move header menus to just above the content
     299            if ($hasTabRoot) {
     300                $pos_ = $movPosIfTabs[$pos_] ?? $pos_;
     301            }
     302            if ($items_) {
     303                $ret_ = new Menu();
     304                foreach ($items_ as $item) {
     305                    $ret_->add(50, $item);
     306                }
     307                $ret_ = $this->ui->render($ret_, $request);
     308                if (strlen($ret_)) {
     309                    $menus[$pos_] = $ret_;
     310                }
     311            }
     312        }
     313
     314    // BUILD: REUSE MENUS IN HEADER IF THERE'S TABROOT
     315        if ($hasTabRoot) {
     316            $path_ = $tabRoot;
     317            $allItems_ = $this->pageMap->getMenuItems($path_);
     318            foreach ($allItems_ as $pos_ => $items_) {
     319                if (!isset($movPosIfTabs[$pos_])) {
     320                    continue;
     321                }
     322                $ret_ = new Menu();
     323                foreach ($items_ as $item) {
     324                    $ret_->add(50, $item);
     325                }
     326                $ret_ = $this->ui->render($ret_, $tabRootRequest);
     327                if (strlen($ret_)) {
     328                    $menus[$pos_] = $ret_;
     329                }
     330            }
     331        }
     332
     333    // BUILD: TITLE
     334        $path_ = $hasTabRoot ? $tabRoot : $path;
     335        $title = $this->pageMap->getTitle($path_);
     336        if ($title) {
     337            $pos_ = PageMap::POS_TITLE;
     338            $ret_ = (new HtmlElement('h1'))->append($title);
     339            if (isset($menus[$pos_])) {
     340                $ret_ = new InlineList([$ret_, $menus[$pos_]]);
     341                unset($menus[$pos_]);
     342            }
     343            $ret[] = [$pos_, $ret_];
     344        }
     345
     346    // ADD MENUS
     347        foreach ($menus as $pos_ => $ret_) {
     348            $ret[] = [$pos_, $ret_];
     349        }
     350
     351    // ADDITIONAL SECTIONS
     352        $sections_ = $this->pageMap->getSections($path);
     353// _print_r($sections_);
     354// exit;
     355
     356        foreach ($sections_ as $e_) {
     357            list($pos_, $ret_) = $e_;
     358            if ($hasTabRoot) {
     359                $pos_ = $movPosIfTabs[$pos_] ?? $pos_;
     360            }
     361            $ret_ = $this->ui->render($ret_, $request);
     362            if (strlen($ret_)) {
     363                $ret[] = [$pos_, $ret_];
     364            }
     365        }
     366
     367    // ALSO REUSE ADDITIONAL SECTIONS FROM TABROOT IF ANY
     368        if ($hasTabRoot) {
     369            $sections_ = $this->pageMap->getSections($tabRoot);
     370            foreach ($sections_ as $e_) {
     371                list($pos_, $ret_) = $e_;
     372                if (!isset($movPosIfTabs[$pos_])) {
     373                    continue;
     374                }
     375                $ret_ = $this->ui->render($ret_, $tabRootRequest);
     376                if (strlen($ret_)) {
     377                    $ret[] = [$pos_, $ret_];
     378                }
    232379            }
    233380        }
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Middleware/UiMiddleware.php

    r3394866 r3400983  
    3636
    3737        if (strlen($requestedLayoutId)) {
    38             if (is_object($ret) && ($ret instanceof Main)) {
    39                 $ret->setLayoutId($requestedLayoutId);
    40             }
     38            $request = $request->withAttribute('layoutId', $requestedLayoutId);
    4139        }
    4240
  • plain-event-calendar/trunk/vendor/plainware/ui/src/PageMap.php

    r3394866 r3400983  
    1010
    1111    /**
    12     * Contains menu items: at the bottom of the page
    13     */
    14     private array $navs = [];
    15 
    16     /**
    17     * Contains nav items: at the top of the page
    18     */
    19     private array $menus = [];
    20 
    21     /**
    22     * Contains tab items
    23     */
    24     private array $tabs = [];
    25 
    26     /**
    27     * Contains breadcrumbs titles
    28     */
    29     private array $breadcrumbTitles = [];
    30 
    31     /**
    32     * Contains roots
    33     */
    34     private array $roots = [];
    35 
    36     /**
    37     * Contains tab roots
    38     */
    39     private array $tabRoots = [];
     12    * Generic container for items, use key for types
     13    */
     14    private array $items = [];
    4015
    4116    /**
     
    4318    */
    4419    private string $tempRoute = '';
     20    private int $tempPos = self::POS_CONTENT;
     21    private array $parseHashRoute = [];
     22
     23    private const NAV = 1;
     24    private const MENU = 2;
     25    private const TAB = 3;
     26    private const TITLE = 4;
     27    private const BREADCRUMBTITLE = 5;
     28    private const ROOT = 6;
     29    private const SECTION = 7;
     30    private const PARENT_SECTION = 8;
     31    private const CONTENT = 9;
     32
     33    public const POS_TOP = 10;
     34    public const POS_MAINMENU = 20;
     35    public const POS_BREADCRUMBS = 25;
     36    public const POS_TITLE = 30;
     37    public const POS_FLASHMESSAGE = 35;
     38    public const POS_MENU = 40;
     39    public const POS_TABS = 50;
     40    public const POS_SUBTITLE = 60;
     41    public const POS_SUBMENU = 70;
     42    public const POS_CONTENT = 80;
     43    public const POS_BOTTOM = 90;
     44
     45    public function posTop(): self
     46    {
     47        $this->tempPos = self::POS_TOP;
     48
     49        return $this;
     50    }
     51
     52    public function posMainMenu(): self
     53    {
     54        $this->tempPos = self::POS_MAINMENU;
     55
     56        return $this;
     57    }
     58
     59    public function posTitle(): self
     60    {
     61        $this->tempPos = self::POS_TITLE;
     62
     63        return $this;
     64    }
     65
     66    public function posMenu(): self
     67    {
     68        $this->tempPos = self::POS_MENU;
     69
     70        return $this;
     71    }
     72
     73    public function posAfterContent(): self
     74    {
     75        $this->tempPos = self::POS_CONTENT + 1;
     76
     77        return $this;
     78    }
     79
     80    public function posBeforeContent(): self
     81    {
     82        $this->tempPos = self::POS_CONTENT - 1;
     83
     84        return $this;
     85    }
     86
     87    public function posBottom(): self
     88    {
     89        $this->tempPos = self::POS_CONTENT - 1;
     90
     91        return $this;
     92    }
    4593
    4694    /**
     
    56104    }
    57105
     106    public function whenPartial(string $route, string $hash): self
     107    {
     108        $this->tempRoute = $route . '#' . $hash;
     109
     110        return $this;
     111    }
     112
    58113    /**
    59114    *
     
    63118    public function makeRoot(): self
    64119    {
    65         $route = $this->tempRoute;
    66         $this->roots[$route] = $route;
    67 
    68         return $this;
     120        return $this->setItem(static::ROOT, true);
    69121    }
    70122
     
    74126    *
    75127    */
    76     public function isRoot(string $route): bool
    77     {
    78         return isset($this->roots[$route]) ? true : false;
    79     }
    80 
    81     /**
    82     *
    83     * Make the route be tabRoot: this is where the tab menu and header title is displayed
    84     *
    85     */
    86     public function makeTabRoot($headerTitle): self
    87     {
    88         $route = $this->tempRoute;
    89         $this->tabRoots[$route] = $headerTitle;
    90 
    91         return $this;
     128    public function isRoot(string $path): bool
     129    {
     130        list($route,) = $this->router->parsePath($path);
     131        if (null === $route) {
     132            return false;
     133        }
     134
     135        return isset($this->items[$route][static::ROOT]) ? true : false;
    92136    }
    93137
     
    102146    *
    103147    */
    104     public function isTabRoot(string $route): bool
    105     {
     148    public function isTabRoot(string $path): bool
     149    {
     150        return (null === $this->getItem(static::TAB, $path)) ? false : true;
     151    }
     152
     153    /**
     154    *
     155    * Adds a section to a page.
     156    *
     157    */
     158    public function addSection($section): self
     159    {
     160        $pos = $this->tempPos;
     161        $this->resetPos();
     162
     163        return $this->addItemToCollection(static::SECTION, $section, $pos);
     164    }
     165
     166    /**
     167    *
     168    * Adds a parent section, i.e. it applies to the route and all children
     169    *
     170    */
     171    public function addParentSection($section): self
     172    {
     173        $pos = $this->tempPos;
     174        $this->resetPos();
     175
     176        return $this->addItemToCollection(static::PARENT_SECTION, $section, $pos);
     177    }
     178
     179    /**
     180    *
     181    * Gets page sections
     182    *
     183    */
     184    public function getSections(string $path): array
     185    {
     186        $ret = [];
     187
     188        list($route,) = $this->router->parsePath($path);
     189        if (null === $route) {
     190            return $ret;
     191        }
     192
     193    // find exact
     194        $type = static::SECTION;
    106195        $allRoutes = $this->allRoutes($route);
    107196        foreach ($allRoutes as $route_) {
    108             if (isset($this->tabRoots[$route_])) {
    109                 return true;
    110             }
    111         }
    112 
    113         return false;
    114     }
    115 
    116     /**
    117     *
    118     * Get tab root title
    119     *
    120     */
    121     public function getTabRootTitle(string $route)
    122     {
     197            if (isset($this->items[$route_][$type])) {
     198                $ret = array_merge($ret, $this->items[$route_][$type]);
     199            }
     200        }
     201
     202    // find all parent sections
     203        $type = static::PARENT_SECTION;
     204        $routeStack = $this->router->findAllRouteStack($route);
     205        foreach ($routeStack as $route_) {
     206            if (isset($this->items[$route_][$type])) {
     207                $ret = array_merge($ret, $this->items[$route_][$type]);
     208            }
     209        }
     210
     211        if (!$ret) {
     212            return $ret;
     213        }
     214        uasort($ret, [$this, 'sortItems']);
     215
     216        return $ret;
     217    }
     218
     219    /**
     220    *
     221    * Links to another page from tab menu
     222    *
     223    */
     224    public function addTabItem($item, int $order = 50): self
     225    {
     226        return $this->addItemToCollection(static::TAB, $item, $order);
     227    }
     228
     229    /**
     230    *
     231    * Gets tabs options
     232    *
     233    */
     234    public function getTabItems(string $route): array
     235    {
     236        return $this->getItemCollection(static::TAB, $route);
     237    }
     238
     239    private function addItemToCollection(string $type, $item, int $order = 50): self
     240    {
     241        $route = $this->tempRoute;
     242
     243        if (!isset($this->items[$route][$type])) {
     244            $this->items[$route][$type] = [];
     245        }
     246        $this->items[$route][$type][] = [$order, $item];
     247
     248        return $this;
     249    }
     250
     251    private function getItemCollection(int $type, string $path, bool $strip = true): array
     252    {
     253        $ret = [];
     254
     255        list($route,) = $this->router->parsePath($path);
     256        if (null === $route) {
     257            return $ret;
     258        }
     259
    123260        $allRoutes = $this->allRoutes($route);
    124261        foreach ($allRoutes as $route_) {
    125             if (isset($this->tabRoots[$route_])) {
    126                 return $this->tabRoots[$route_];
    127             }
    128         }
    129 
    130         return false;
    131     }
    132 
    133     /**
    134     *
    135     * Links to another page from tab menu
    136     *
    137     */
    138     public function addTabItem($menuItem, int $order = 50): self
    139     {
    140         $fromRoute = $this->tempRoute;
    141         if (!isset($this->tabRoots[$fromRoute])) {
    142             throw new \InvalidArgumentException('This route is not a tab root: ' . $fromRoute);
    143         }
    144 
    145         if (!isset($this->tabs[$fromRoute])) {
    146             $this->tabs[$fromRoute] = [];
    147         }
    148         $this->tabs[$fromRoute][] = [$menuItem, $order];
    149 
    150         return $this;
    151     }
    152 
    153     /**
    154     *
    155     * Gets tabs options
    156     *
    157     */
    158     public function getTabItems(string $route): array
    159     {
    160         $ret = [];
    161 
     262            if (isset($this->items[$route_][$type])) {
     263                $ret = array_merge($ret, $this->items[$route_][$type]);
     264            }
     265        }
     266
     267        if (!$ret) {
     268            return $ret;
     269        }
     270        uasort($ret, [$this, 'sortItems']);
     271
     272        return $strip ? array_column($ret, 1) : $ret;
     273    }
     274
     275    private function getItem(int $type, string $path)
     276    {
     277        list($route,) = $this->router->parsePath($path);
     278        if (null === $route) {
     279            return null;
     280        }
     281
     282        return $this->getItemByRoute($type, $route);
     283    }
     284
     285    private function getItemByRoute(int $type, string $route)
     286    {
    162287        $allRoutes = $this->allRoutes($route);
    163288        foreach ($allRoutes as $route_) {
    164             if (isset($this->tabs[$route_])) {
    165                 $ret = array_merge($ret, $this->tabs[$route_]);
    166             }
    167         }
    168 
    169         if (!$ret) {
    170             return $ret;
    171         }
    172 
    173         usort($ret, [$this, 'sortItems']);
    174 
    175         return array_column($ret, 0);
     289            if (isset($this->items[$route_][$type])) {
     290                return $this->items[$route_][$type];
     291            }
     292        }
     293
     294        return null;
     295    }
     296
     297    private function setItem(int $type, $item = null): self
     298    {
     299        $route = $this->tempRoute;
     300        $this->items[$route][$type] = $item ?? $route;
     301
     302        return $this;
    176303    }
    177304
     
    181308    *
    182309    */
    183     public function addMenuItem($menuItem, int $order = 50): self
     310    public function addMenuItem($item, int $itemPos = 50): self
    184311    {
    185312        $route = $this->tempRoute;
    186 
    187         if (!isset($this->menus[$route])) {
    188             $this->menus[$route] = [];
    189         }
    190         $this->menus[$route][] = [$menuItem, $order];
    191 
    192         return $this;
    193     }
    194 
    195     /**
    196     *
    197     * Gets menu options
    198     *
    199     */
    200     public function getMenuItems(string $route): array
     313        $pos = $this->tempPos;
     314        $type = static::MENU;
     315
     316        if (!isset($this->items[$route])) {
     317            $this->items[$route] = [];
     318        }
     319        if (!isset($this->items[$route][$type])) {
     320            $this->items[$route][$type] = [];
     321        }
     322        if (!isset($this->items[$route][$type][$pos])) {
     323            $this->items[$route][$type][$pos] = [];
     324        }
     325
     326        $this->items[$route][$type][$pos][] = [$itemPos, $item];
     327
     328        $this->resetPos();
     329
     330        return $this;
     331    }
     332
     333    private function resetPos(): self
     334    {
     335        $this->tempPos = self::POS_CONTENT;
     336
     337        return $this;
     338    }
     339
     340    public function getMenuItems(string $path): array
    201341    {
    202342        $ret = [];
     343
     344        list($route,) = $this->router->parsePath($path);
     345        if (null === $route) {
     346            return $ret;
     347        }
     348
     349        return $this->getMenuItemsByRoute($route);
     350    }
     351
     352    private function getMenuItemsByRoute(string $route): array
     353    {
     354        $ret = [];
     355
     356        $type = static::MENU;
    203357
    204358        $allRoutes = $this->allRoutes($route);
    205359        foreach ($allRoutes as $route_) {
    206             if (isset($this->menus[$route_])) {
    207                 $ret = array_merge($ret, $this->menus[$route_]);
     360            if (isset($this->items[$route_][$type])) {
     361                foreach ($this->items[$route_][$type] as $menuPos => $items_) {
     362                    if (!isset($ret[$menuPos])) {
     363                        $ret[$menuPos] = [];
     364                    }
     365                    $ret[$menuPos] = array_merge($ret[$menuPos], $items_);
     366                }
    208367            }
    209368        }
     
    213372        }
    214373
    215         usort($ret, [$this, 'sortItems']);
    216 
    217         return array_column($ret, 0);
    218     }
    219 
    220     /**
    221     *
    222     * Links to another page from nav
    223     *
    224     */
    225     public function addNavItem($item, int $order = 50): self
    226     {
    227         $route = $this->tempRoute;
    228         if (!isset($this->navs[$route])) {
    229             $this->navs[$route] = [];
    230         }
    231         $this->navs[$route][] = [$item, $order];
    232 
    233         return $this;
    234     }
    235 
    236     /**
    237     *
    238     * Gets nav options
    239     *
    240     */
    241     public function getNavItems(string $route): array
     374        foreach (array_keys($ret) as $k) {
     375            uasort($ret[$k], [$this, 'sortItems']);
     376            $ret[$k] = array_column($ret[$k], 1);
     377        }
     378
     379        return $ret;
     380    }
     381
     382    public function getPartialMenuItems(string $path, string $hash): array
    242383    {
    243384        $ret = [];
    244385
    245         $allRoutes = $this->allRoutes($route);
    246         foreach ($allRoutes as $route_) {
    247             if (isset($this->navs[$route_])) {
    248                 $ret = array_merge($ret, $this->navs[$route_]);
    249             }
    250         }
    251 
    252         if (!$ret) {
    253             return $ret;
    254         }
    255 
    256         usort($ret, [$this, 'sortItems']);
    257 
    258         return array_column($ret, 0);
     386        list($route,) = $this->router->parsePath($path);
     387        if (null === $route) {
     388            return $ret;
     389        }
     390
     391        list($hashPattern, $hashParams) = $this->parseHash($hash);
     392        if (null === $hashPattern) {
     393            return $ret;
     394        }
     395
     396        return $this->getMenuItemsByRoute($route . '#' . $hashPattern);
    259397    }
    260398
     
    266404    public function setBreadcrumbTitle($title): self
    267405    {
    268         $route = $this->tempRoute;
    269         $this->breadcrumbTitles[$route] = $title;
    270 
    271         return $this;
     406        return $this->setItem(static::BREADCRUMBTITLE, $title);
     407    }
     408
     409    public function setContent($v): self
     410    {
     411        return $this->setItem(static::CONTENT, $v);
    272412    }
    273413
     
    277417    *
    278418    */
    279     public function getBreadcrumbTitle(string $route)
    280     {
    281         $allRoutes = $this->allRoutes($route);
    282         foreach ($allRoutes as $route_) {
    283             if (isset($this->breadcrumbTitles[$route_])) {
    284                 return $this->breadcrumbTitles[$route_];
    285             }
    286         }
    287 
    288         return '';
     419    public function getBreadcrumbTitle(string $path)
     420    {
     421        return $this->getItem(static::BREADCRUMBTITLE, $path) ?? '';
     422    }
     423
     424    public function setTitle($title): self
     425    {
     426        return $this->setItem(static::TITLE, $title);
     427    }
     428
     429    public function getTitle(string $path)
     430    {
     431        return $this->getItem(static::TITLE, $path) ?? '';
     432    }
     433
     434    private function getTitleByRoute(string $route)
     435    {
     436        return $this->getItemByRoute(static::TITLE, $route) ?? '';
     437    }
     438
     439    public function getPartialTitle(string $path, string $hash)
     440    {
     441        $ret = '';
     442
     443        list($route,) = $this->router->parsePath($path);
     444        if (null === $route) {
     445            return $ret;
     446        }
     447
     448        list($hashPattern, $hashParams) = $this->parseHash($hash);
     449        if (null === $hashPattern) {
     450            return $ret;
     451        }
     452
     453        return $this->getTitleByRoute($route . '#' . $hashPattern);
     454    }
     455
     456    public function getPartialContent(string $path, string $hash)
     457    {
     458        $ret = '';
     459
     460        list($route,) = $this->router->parsePath($path);
     461        if (null === $route) {
     462            return $ret;
     463        }
     464
     465        list($hashPattern, $hashParams) = $this->parseHash($hash);
     466        if (null === $hashPattern) {
     467            return $ret;
     468        }
     469
     470        return $this->getItemByRoute(static::CONTENT, $route . '#' . $hashPattern) ?? '';
    289471    }
    290472
    291473    private function sortItems(array $a, array $b)
    292474    {
    293         return ($a[1] - $b[1]);
     475        return ($a[0] - $b[0]);
     476    }
     477
     478/**
     479 * Finds the route pattern with array of parameters from an input route.
     480 * Example:
     481 * customer/12/edit => [customer/{id}/edit, [id] => 12]
     482 *
     483 * @param string
     484 * @return array
     485 */
     486    public function parseHash(string $hash): array
     487    {
     488        if (isset($this->parsedHashCache[$hash])) {
     489            return $this->parsedHashCache[$hash];
     490        }
     491
     492        $ret = null;
     493        foreach (array_keys($this->items) as $routePattern) {
     494            $pos = strpos($routePattern, '#');
     495            if (false === $pos) {
     496                continue;
     497            }
     498
     499            $hashRoutePattern = substr($routePattern, $pos + 1);
     500            $params = $this->router->getRouteParams($hash, $hashRoutePattern);
     501            if (null !== $params) {
     502                $ret = [$hashRoutePattern, $params];
     503                break;
     504            }
     505        }
     506
     507        if (null === $ret) {
     508            $ret = [null, []];
     509        }
     510
     511        $this->parsedHashCache[$hash] = $ret;
     512
     513        return $ret;
    294514    }
    295515}
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Renderer/Form/TimeInputRenderer.php

    r3394866 r3400983  
    88use Plainware\Ui\HtmlHelper;
    99use Plainware\Ui\Ui;
    10 use Plainware\Time\Time;
     10use Plainware\DateTime\DateTimeFormatter;
    1111
    1212class TimeInputRenderer
    1313{
    1414    public $ui = Ui::class;
    15     public Time $t;
     15    public $dtf = DateTimeFormatter::class;
    1616
    1717    public function __invoke(TimeInput $el, Request $request): string
     
    2929        $today = 20250710;
    3030        $options = [];
     31
     32        $dt = new \DateTime();
    3133        foreach (range(0, 23, 1) as $k) {
    3234            $v = $k;
    3335
    34             $k2 = $this->t->fromDateSeconds($today, $k * 60 * 60);
    35             $v2 = $this->t->formatHour($k2);
     36            $dt->setTime($k, 0);
     37            $v2 = $this->dtf->formatHour($dt);
    3638
    3739            $v = sprintf('%02d', $k);
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Renderer/FormRenderer.php

    r3394866 r3400983  
    255255    for (var i = 0; i < toggles.length; i++) {
    256256        var listen = toggles[i][0];
    257         var selectedValue = container.querySelector('input[name=' + listen.name + ']:checked').value;
     257        var checkedOne = container.querySelector('input[name=' + listen.name + ']:checked');
     258        var selectedValue = checkedOne ? checkedOne.value : false;
    258259        if (selectedValue == listen.value) {
    259260            for (var j = 1; j < toggles[i].length; j++) {
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Renderer/LinkRenderer.php

    r3394866 r3400983  
    66use Plainware\Core\App;
    77use Plainware\Http\Uri;
     8use Plainware\Http\RequestFactory;
    89use Plainware\Http\Request;
    910use Plainware\Http\Router;
     
    1718    public $uri = Uri::class;
    1819    public $router = Router::class;
     20    public $requestFactory = RequestFactory::class;
    1921
    2022    public function __invoke(Link $el, Request $request): string
     
    4446                $href = $fullUri;
    4547            } else {
    46                 $route = $el->getSlug();
    47                 if (null !== $route) {
     48                $path = $el->getPath();
     49                if (null !== $path) {
    4850                    $params = $el->getParams();
    49 
    50                     $passParams = [];
    51                     if ($request) {
    52                         $myRequest = $request->makeTo($route, $params);
    53                         $route = $myRequest->getRoute();
    54                         $params = $myRequest->getQueryParams();
    55                         list($parsedRoute_, $routeParams_) = $this->router->parseRoute($route);
    56                         foreach ($routeParams_ as $k => $v) {
    57                             $myRequest = $myRequest->withRouteParam($k, $v);
    58                         }
    59                     }
    60 
    61                     $href = $this->uri->to($route, $params);
     51                    $path = $request->resolvePath($path);
     52                    $href = $this->uri->to($path, $params);
    6253                }
    6354            }
     
    6556
    6657        $label = $el->getLabel();
    67         if ((is_callable($label) or is_array($label)) && $myRequest) {
     58        if ((is_callable($label) or is_array($label)) && (null !== $path)) {
     59            $request_ = $this->requestFactory->from($request, $path, $params);
    6860            $label = $this->app->makeFunc($label);
    69             $label = call_user_func($label, $myRequest);
     61            $label = call_user_func($label, $request_);
    7062        }
    7163        $label = $this->ui->render($label, $request);
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Renderer/MainMenuRenderer.php

    r3394866 r3400983  
    88use Plainware\Ui\Component\MainMenu;
    99use Plainware\Ui\Component\Link;
     10use Plainware\Ui\Renderer\MenuRenderer;
    1011
    1112class MainMenuRenderer
    1213{
    1314    public $ui = Ui::class;
     15    public $menuRenderer = MenuRenderer::class;
    1416
    1517    public function __invoke(MainMenu $el, Request $request)
     
    2325
    2426        $items = $el->getItems();
     27        $items = array_filter($items, fn($e) => $this->menuRenderer->isItemAllowed($e, $request));
    2528        if (!$items) {
    2629            return $ret;
    2730        }
    2831
    29         $currentSlug = $el->getCurrentSlug() ?? '';
    30         $currentSlugArray = strlen($currentSlug) ? explode('/', $currentSlug) : [];
     32        $currentPath = $el->getCurrentPath() ?? '';
     33        $currentPathArray = strlen($currentPath) ? explode('/', $currentPath) : [];
    3134
    3235        $ret = new HtmlElement('ul');
     
    3942                $item->setMenu(true);
    4043
    41                 $request_ = $request->makeTo($item->getSlug(), $item->getParams());
    42                 $thisSlug = $request_->getSlug();
    43                 $thisSlugArray = strlen($thisSlug) ? explode('/', $thisSlug) : [];
     44                $path_ = $request->resolvePath($item->getPath());
     45                $pathArray_ = strlen($path_) ? explode('/', $path_) : [];
    4446
    45                 $len = min(count($thisSlugArray), count($currentSlugArray));
     47                $len = min(count($pathArray_), count($currentPathArray));
    4648                $isOn = true;
    4749                for ($i_ = 0; $i_ < $len; $i_++) {
    48                     if ($thisSlugArray[$i_] !== $currentSlugArray[$i_]) {
     50                    if ($pathArray_[$i_] !== $currentPathArray[$i_]) {
    4951                        $isOn = false;
    5052                        break;
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Renderer/MainRenderer.php

    r3394866 r3400983  
    66use Plainware\Http\Request;
    77use Plainware\Ui\Component\Main;
    8 use Plainware\Ui\Component\Link;
    9 use Plainware\Ui\Component\InlineList;
    108use Plainware\Ui\Component\HtmlElement;
    11 use Plainware\Ui\Component\Container;
    12 use Plainware\Ui\Component\FlashMessage;
    13 use Plainware\Ui\Component\MainMenu;
    14 use Plainware\Ui\Component\Breadcrumbs;
    15 use Plainware\Ui\Renderer\ContainerRenderer;
    169use Plainware\Ui\Ui;
    1710
     
    2013    public $translate = Translate::class;
    2114    public $ui = Ui::class;
    22     public $containerRenderer = ContainerRenderer::class;
    2315
    2416    public function __invoke(Main $el, Request $request)
     
    2719            ->withAttr('id', 'pw2')
    2820            ;
    29         $el->wrapBy($wrap);
    30 
    31     // title with nav
    32         $title = $el->getTitle();
    33         $nav = $el->getNav();
    34 
    35         if ($title) {
    36             $title = (new HtmlElement('h1'))->append($title);
    37         }
    38 
    39         $v = null;
    40         if ($title && $nav->hasItems()) {
    41             $v = new InlineList([$title, $nav]);
    42         } elseif ($title) {
    43             $v = $title;
    44         } elseif ($nav->hasItems()) {
    45             $v = $nav;
    46         }
    47         if ($v) {
    48             $v = (new HtmlElement('div'))
    49                 ->append($v)
    50                 ->withAttr('class', 'pw-header-title')
    51                 ;
    52             $el->add(25, $v);
    53         }
    54 
    55     // flash messages
    56         $v = null;
    57         $errorMessages = $el->getErrorMessages();
    58         $messages = $el->getMessages();
    59 
    60         if ($errorMessages or $messages) {
    61             $v = new Container();
    62             foreach ($errorMessages as $msg) {
    63                 $msg = (new FlashMessage($msg))->setError();
    64                 $v->add(35, $msg);
    65             }
    66             foreach ($messages as $msg) {
    67                 $msg = new FlashMessage($msg);
    68                 $v->add(35, $msg);
    69             }
    70 
    71             $v = (new HtmlElement('div'))
    72                 ->append($v)
    73                 ->withAttr('class', 'pw-header-flashmessage')
    74                 ;
    75             $el->add(35, $v);
    76         }
    77 
    78     // pass to container rendering
    79         $ret = ($this->containerRenderer)($el);
    80         $ret = $this->ui->render($ret, $request);
     21        $ret = $wrap->append($el->getContent());
     22        $ret = $this->ui->render($wrap, $request);
    8123
    8224    // translate
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Renderer/MenuRenderer.php

    r3394866 r3400983  
    33namespace Plainware\Ui\Renderer;
    44
     5use Plainware\Http\Request;
     6use Plainware\Http\Server;
     7use Plainware\Http\RequestFactory;
    58use Plainware\Ui\Component\HtmlElement;
    69use Plainware\Ui\Component\Menu;
     
    1114{
    1215    public $ui = Ui::class;
     16    public $requestFactory = RequestFactory::class;
     17    public $server = Server::class;
    1318
    14     public function __invoke(Menu $el)
     19    public function __invoke(Menu $el, Request $request)
    1520    {
    1621        $ret = '';
     
    2227
    2328        $items = $el->getItems();
     29        $items = array_filter($items, fn($e) => $this->isItemAllowed($e, $request));
    2430        if (!$items) {
    2531            return $ret;
     
    4551        return $ret;
    4652    }
     53
     54    public function isItemAllowed($item, Request $request): bool
     55    {
     56        if (!($item instanceof Link)) {
     57            return true;
     58        }
     59
     60        $request_ = $this->requestFactory->from($request, $item->getPath(), $item->getParams());
     61        $can = $this->server->can($request_);
     62        $ret = (false === $can) ? false : true;
     63
     64        return $ret;
     65    }
    4766}
  • plain-event-calendar/trunk/vendor/plainware/ui/src/Renderer/TabMenuRenderer.php

    r3394866 r3400983  
    88use Plainware\Ui\Component\TabMenu;
    99use Plainware\Ui\Component\Link;
     10use Plainware\Ui\Renderer\MenuRenderer;
    1011
    1112class TabMenuRenderer
    1213{
    1314    public $ui = Ui::class;
     15    public $menuRenderer = MenuRenderer::class;
    1416
    1517    public function __invoke(TabMenu $el, Request $request): string
    1618    {
    1719        $items = $el->getItems();
     20        $items = array_filter($items, fn($e) => $this->menuRenderer->isItemAllowed($e, $request));
     21
    1822        if (!$items) {
    1923            $ret = '';
Note: See TracChangeset for help on using the changeset viewer.