Giter VIP home page Giter VIP logo

keeper's Introduction

Keeper - Data Portal Framework

Latest Stable Version Build Status Codecov

Keeper is portal engine and UI framework (based on Stempler). Framework includes various elements such as dynamic forms, data grids, tabs and other layout partials. You can use Keeper to create complex Admin panels, User panels and other applications.

All the Keeper functionality integrated with spiral/security and follows RBAC rules.

Keeper Demo

Configuration can be performed via code or annotations.

Demo project is available here.

License:

MIT License (MIT). Please see LICENSE for more information. Maintained by Spiral Scout.

keeper's People

Contributors

andrewkirkovski avatar butschster avatar msmakouz avatar vvval avatar wolfy-j avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

msmakouz

keeper's Issues

Ability to refresh data in table

It will be useful if ui:grid will have optional ability to refresh data in grid.
Also it will be great if it can be triggered in js if it will be required

cycle and guar interceptors order

if a rule for a guard has a context which is expected to be received from the previously called cycle interceptor it will fail because the interceptors' order is changed and guard rules do not have id=>entity replaced content

Fix GraphSorter

In case when you are working with Keeper controllers you can met some strange error with message like:
[ErrorException] Undefined index: index in /var/www/vendor/spiral/helpers/src/GraphSorter.php:80

It looks as a big problem but in reality it is simple error with annotated controller when you define not existing parent for sitemap:
@Keeper\Sitemap\View(title="Create Client", parent="index")

In described example controller has no action index and it is a root of the issue.

[UI:Grid] reusable renderers

It is very often when you need to use similar renderer in different tables in your project

Now if you will try to define similar renderer in different places it throws an exception like Error: 'caseStatus' is unknown renderer

It will be great if you can define such renderers in one place and use it across whole project when you need it.

Could you describe how to solve the problem?

Proposal

Action annotation

@Action has only route and methods params. Can't use: group, middleware, defaults and name

While adding defaults and middleware is quite useful,
I don't know where group is applicable here, but it should be.

Also, name might be very helpful. The main difference between standard @Route and keeper @Controller+@Action
is that @Controller allows to set a global actions' prefix and a namespace.

Since we have to use a unique route name
(my:custom/endpoint-route.name in standard @Route and controller.action in @Action)
I see no use of having automatic imploding controller action reference into a single string (like controller.action),
we can use a standard @Route naming (but still apply the prefix from the @Controller)

So the proposal is:

  • @Controller namespace refers to a global prefix (declared somewhere where the namespace is added in the bootloader)
  • @Controller prefix refers to a local prefix (any empty value is equal to '')
  • no slashes are auto added or trimmed (except of /\/+/ to remove abc///de/).
    This will mean a keeper controller action may look like this: keeper_users-edit (this is fully up to a developer)
  • no defaults will be automatically suggested by having prefix=''
    • a default controller CAN be assigned to a namespace explicitly
    • a default method CAN be declared in the @Controller annotation explicitly (by default index)
    • Otherwise, no defaults
  • any other standard @Route params are applicable. Even using a group param will act fully the same,
    but on the action level - if a group also has a prefix it will be added right between @Controller prefix and @Action route

According to that, the usage will look like the next:

<?php
//...
class KeeperBootloader//...
{
    public function register(string $namespace, string $prefix = null, string $controllerDefault = null): void
    {
        //...
    }

    public function boot(): void
    {
        $this->register('keeper', 'admin/'); //no default controller
        $this->register('profile', 'me/', 'dashboard'); //'dashboard' is the default controller
    }
}
/**
 * @\Spiral\Keeper\Annotation\Controller(namespace="keeper", prefix="people/", defaultAction="stats")
 */
class Users
{
    /**
     * @\Spiral\Keeper\Annotation\Action(name="users:index", route="users") //leading slash presents in the prefix
     */
    public function index(): string
    {
        return 'list';
    }
}
Each namespace can have the same route names - they will be isolated
<a href="@action('keeper', 'users:index', [])">Refer to a keeper namespace.</a>

Sitemap and breadcrumbs generator

Annotations should be synced with a custom sitemap generation (for example - via bootloader).
If an action is already registered in the bootloader, there should be no need of adding an empty annotation to the method.
Currently, if a developer wants to use a breadcrumb using annotations, they have to use empty (duplicating) annotations.

Also, breadcrumbs can't refer to an external controller. It might be helpful for child controllers.

RBAC optimization

Is it possible to make wildcard more powerfull?

When you are using keeper usually you should use 2 actions for most used operations like:

  1. Items List = list(for receiving items list in grid) + index (display grid)
  2. Create new item = new (form) + create (form processing)
  3. Update item = edit (form) + update (form processing)

If you want to protect your system with RBAC now you can prepare all rules in format like keeper.users.(index|list) and it works for assigning in PermissionsInterface.

But if you want to check such rule in some view like @auth(\App\Security\PermissionList::USER_LIST) with value keeper.users.(index|list) this action will be forbidden for you and it is not comfortable.

As an example:

  1. You have some admin role with next rules
  • keeper.*
  • keeper..
  • keeper...*
  1. You have some template with check some union rule like keeper.users.(index|list)
  2. This check will be forbidden for you

As a possible solution you can use strict rule like keeper.users.list and check will be passed.
But it will double amount of all list endpoints in your system.
Also it is actual for create and edit operations...

Need to render empty nav breadcrumbs block

@if(count($_bc_) !== 0)
<nav class="sf-breadcrumb" aria-label="breadcrumb">
<ul class="sf-breadcrumb__list">
@foreach($_bc_ as $_n_)
@if($_n_->getName() === $_activeRoute_)
<li class="sf-breadcrumb__item active" aria-current="page">{{ $_n_->getOption('title') }}</li>
@elseif(in_array($_n_->getOption('type'), ['segment','group'], true))
<li class="sf-breadcrumb__item">{{ $_n_->getOption('title') }}</li>
@else
<li class="sf-breadcrumb__item"><a
href="{!! $_router_->uri($_n_->getName(), $_args_) !!}">{{ $_n_->getOption('title') }}</a>
</li>
@endif
@endforeach
@php unset($_bc_, $_n_, $_args_); @endphp
</ul>
</nav>
@endif

If breadcrumbs are empty, the page header is moved to the right, as a hack you need to render an empty <nav> block (move if clause to 1 line lower)

Also, the filename has a typo

Select placeholder is send as a value

<form:select
    name="role"
    label="Role"
    values="{{ $roles }}"
    value="2"
    placeholder="Select Role"
/>

is presented as

<select name="role">
    <option value="">Select Role</option>
    <option value="admin">Administrator</option>
    <option value="user">User</option>
</select>

and sent as

{"filter": {"role": "Select Role"}}

The only fix right now is to remove the placeholder and append it to the values list with null key

[Grid] Records management

In current state grids are not fully powerful tool.

It can be more powerful in case you add some simple records management like:

  1. Refresh records (without full page reload)
  2. Add record with in-row format
  3. Edit record with in-row format
  4. Delete record
  5. Send table data (list of records. Can be only for page or full list) for save

Keeper forms can't work in effective mode with Spiral Filter

I've have form prepared for Keeper:

<form:input name="email" label="Email" value="{{ $user->email }}" size="6"/>

I've have request with validation for this optional Field

class UpdateUser extends Filter
{
    protected const SCHEMA = [
        ...
        'email' => 'data:email',
    ];

    protected const VALIDATES = [
        ...,
        'email' => [
            ['string', 'error' => '[[This field should be a string]]'],
            'email',
        ],
    ];

As you can see Filter knows that email field is optional

But Keeper form works by html default behaviour - if field was not filled it sends value as empty string.
Spiral Filter detects that email field exists and start email validation and it fails....

Of course I can prepare double filter class with my specific checker but it is not comfortable.
I've used this filter for client side and there are front can send me these data in any required format - without empty files, etc...

And I would like to reuse this filter but I can't do it now

Ability to open 'Details' in the same tab

Steps to reproduce:

  1. Log in AdminTool.
  2. Open Users page.
  3. Open edit for any entity.

Actual Result: 'Details' links are opening up in a New Tab
Expected Result: 'Details' are opening up in the same tab.

ui:grid header will be very useful

In case when you need to add grid as layout component it will be useful to have ability to add some header (/title) (like ui:panel header).

It will be more comfortable than wrap it by ui:panel

Ability to add title for table

It will be useful if you can add title for your table (ui:grid).
If you have several components on your page it can be required to show what type of data you show in table.

[Form components]: add ability to send default html component params in result component

I've hit with problem that I can't send row param to textarea
I've used code like:

<form:textarea name="someList" label="Some List" rows="7" value="" />

But in result component rows param was skipped:

<div data-field="true" class="form-group col-sm-12 col-md-12 ">
    <label for="i-3ea">Some List</label>
    <textarea id="i-3ea" data-input="true" class="form-control" name="someList" placeholder=""></textarea>
</div>

It will be more suitable to pass default params for origin html-components for form components

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.