Voyager: menu issues

Created on 16 Nov 2017  路  27Comments  路  Source: the-control-group/voyager

  • Laravel Version: 5.4.36
  • Voyager Version:1.0
  • PHP Version: 5.6.27
  • Database Driver & Version: maria db

Description:

First of all, thanks a lot for your work.
I have a problem, when i create a new entrie in my menu, i don't appear on my side-menu

Steps To Reproduce:

Tools->menu builder ->builder -> new menu item
Title ->link type(static url)-> url (it's works) ->icon ->color ->open in (same table/windows)
then update.

bug

Most helpful comment

Thanks Eric!
I've tracked down the issue to:
if(!Auth::user()->can('browse', $item)) {
continue;
} (line 53)

If I comment out the continue then the item shows.

I tried to track down the can function but seems it's not the Voyager.php can function (as it accepts only one param) but rather some more complex use of the Laravel auth can function.

I'm not sure how the "can" function works and how to foll it to allow a non bread item to show.

All 27 comments

There is an issue with the Voyager admin menu where it's checking permissions for EVERY menu item (not just those associated to BREAD), and not displaying it, even if the permissions doesn't exist. So if you link to http://google.com, it will try to figure out what permissions to check, then fail because it obviously won't exist. This should be fixed in one of the next couple releases once we come up with a good way to handle it.

So is there a way to work around it until a fix is released?
I'm facing a similar issue.

Not currently, no.

Can we solve creating a fake bread?

@bedoz, it's really a permissions problem. You can likely work around the issue with a fake BREAD + permissions, but that could get complicated pretty quick.

Ok I think I found a workaround for now, complicated as @fletch3555 says but works.
I created a new permission for my link (key: browse_orders, table: orders) and associate that permission to my user role.
Can that be the right way maybe for also custom link?
Possibilities to create new permissions for custom link from admin menu builder? In this way i can find it in roles editor and select what to do.

The complication is that it's making a best-guess at the right permission to check. If your url is /admin/foo, then it's going to try to look up the DataType with slug = 'foo', then find all permissions that resemble *_foo. If your url is google.com, then that blows up completely.

Thanks for the head's up @fletch3555 , so let say I have "/admin/foo-bar" URL, then I need to put the slug of 'foo_bar' in 'data_types' table. Also I need to add some BREAD permission in 'permissions' table, and later set the role permission (think it could be done via the admin-room Roles menu).

@bimo13 i just create into permission table a row like this key: browse_foo-bar table: foo-bar then into admin roles bread i selected foo-bar browse checkbox. If it work also for you maybe this can be a temporary workaround for this simply url /admin/foo, just until it will be fixed.

I just tried it (what Bedoz suggests). Still can't see the menu item.
Other than the permission table and data_types table entries - are there any other needed entries?

@sysmesh I've managed to make it appear, try what I did on my previous reply. It'll better to make one dummy table and model to put it on data_types and permissions.

Thanks @bimo13 . I tried it but seems I'm missing something.
Here's what I did:

  1. My custom admin page is /admin/fooBar
  2. In data_types table I've added a new line with:
    slug: fooBar, display_name_singular: fooBar, display_name_plural: fooBar, generate _permissions: 1 and the rest is null or default value.
  3. In the permissions table I've added a new line with:
    key: broswe_fooBar, table_name: fooBar
  4. Went to the admin->roles->edit and checked the new fooBar browser permission.
  5. Refreshed the page.

The item is still not showing on the menu.

Am I missing something?

@sysmesh that's odd, that's exactly what I did. Try to create a route inside the voyager 'admin' routes inside the web/routes.php , then try to create a dummy table with a dummy model, and associate it with the custom link.

or, try to not using the camelcase (not sure if it's the problems tho)

@bimo13 - something is strange here...
I've renamed the route to foo_bar
I've ensured that my route is in web.php under the admin group of routes.
I have updated the data types and permission tables to use the slug foo_bar
I also created a dummy foo_bar table

Still nothing.
Are you using the latest Voyager version? Wondering if it's related.
Another idea is that I am not using the default /admin for the admin but I use a custom /manage route.
Maybe the code is hard coded to search for "/admin"?

@sysmesh I doubt such thing will be hardcoded. I'm using the 1.0 version of Voyager. You said that you put your route under the admin group of routes, yet you're using the manage route ??

The "Admin" router group is called "manage" in my web.php so it's the same.
I'm using Voyager 1.0.7 so it might explain the difference in behavior.

@sysmesh, are you using Laravel DebugBar? It will show you what queries are getting run (the one checking permissions), and may indicate what the problem is.

Thanks @fletch3555 . I'll try to turn it on (had it hidden) and see.

So I've enabled debugbar and checked the queries.
data types is looking for the correct slug,
permissions query returns the browse_foo_bar entry.
Yes the menu item doesn't show.
I suspect it's a code check that might be causing this?

That's possible. Look into admin_menu.blade.php, which is the view that renders that menu

Thanks Eric!
I've tracked down the issue to:
if(!Auth::user()->can('browse', $item)) {
continue;
} (line 53)

If I comment out the continue then the item shows.

I tried to track down the can function but seems it's not the Voyager.php can function (as it accepts only one param) but rather some more complex use of the Laravel auth can function.

I'm not sure how the "can" function works and how to foll it to allow a non bread item to show.

->can() is part of Laravel's Auth framework. It calls into the Policies (specifically, MenuItemPolicy). To be even more specific, it calls into BasePolicy::__call(), which calls back to MenuItemPolicy::checkPermission()

Hi @sysmesh , I just realized that when I stashed the routes/web.php, my custom menu-items is missing. You might want to re-check the routing for your custom menu-items, also the controllers for it.

And if you think the problem is with the "slug", have you tried changing it ?

I don't know if it helps you to figured it out, but my inputs on the Menu Builder fields is :
Title of the Menu Item : Foo Bars
Link type : _Dynamic Route_
Route for the menu item : admin.foo-bars
and I don't fill on the rest of it.

and then on the data_types table. I added one row :
name : foo_bars
slug : foo-bars
display_name_singular : Foo Bar
display_name_plural : Foo Bars
icon : null
model_name : App\FooBar (_I said I created a dummy model and table for it_)
policy_name : null
controller : null
description : null
generate_permission : 1
server_side : 0
_timestamps_ : _just put current timestamps there_

after that, I added some rows on the permissions table :
key : browse_foo_bars
table_name : foo_bars (_I said I created a dummy model and table for it_)
_timestamps_ : _just put current timestamps there_
===== Continue for read, edit, add and delete

please mind the s for plural and singular terms there, it might affects the result, not sure tho.

Thanks for being so specific and detailed! I tried it just now but it still doesn't show.

I just gave up and created a local copy of admin_menu.blade.php under my resources/view/vendor/voyager/menu folder.

I hacked the code so that if I add __ in the begining of the link URL it will bypass the security check.
That works :-) Not idle but will solve the issue for now I guess until a proper fix is released.

Here's my updated admin_menu.blade.php code:

<ul class="nav navbar-nav">

@php
    if (Voyager::translatable($items)) {
        $items = $items->load('translations');
    }
@endphp

@foreach ($items as $item)
    @php
        $listItemClass = [];
        $styles = null;
        $linkAttributes = null;
        $transItem = $item;

        if (Voyager::translatable($item)) {
          $transItem = $item->translate($options->locale);
        }

      $href = $item->link();

        if(strpos($href,'__')===0) { // Gilad - Special handling for custom urls
            $allowAnyway = true;
            $href = substr($href,2);
        }
        else $allowAnyway = false;

        // Current page
        if(url($href) == url()->current()) {
            array_push($listItemClass, 'active');
        }

        $permission = '';
        $hasChildren = false;

        // With Children Attributes
        if(!$item->children->isEmpty())
        {
            foreach($item->children as $child)
            {
                if($allowAnyway) $hasChildren = $hasChildren || true; // Gilad - Hacked to allow custom urls
                else $hasChildren = $hasChildren || Auth::user()->can('browse', $child);

                if(url($child->link()) == url()->current())
                {
                    array_push($listItemClass, 'active');
                }
            }
            if (!$hasChildren) {
                continue;
            }

            $linkAttributes = 'href="#' . str_slug($transItem->title, '-') .'-dropdown-element" data-toggle="collapse" aria-expanded="'. (in_array('active', $listItemClass) ? 'true' : 'false').'"';
            array_push($listItemClass, 'dropdown');
        }
        else
        {
            $linkAttributes =  'href="' . url($href) .'"';

            if(!Auth::user()->can('browse', $item) && !$allowAnyway) {
                continue;
            }
        }
    @endphp

    <li class="{{ implode(" ", $listItemClass) }}">
        <a {!! $linkAttributes !!} target="{{ $item->target }}">
            <span class="icon {{ $item->icon_class }}"></span>
            <span class="title">{{ $transItem->title }}</span>
        </a>
        @if($hasChildren)
            <div id="{{ str_slug($transItem->title, '-') }}-dropdown-element" class="panel-collapse collapse {{ (in_array('active', $listItemClass) ? 'in' : '') }}">
                <div class="panel-body">
                    @include('voyager::menu.admin_menu', ['items' => $item->children, 'options' => $options, 'innerLoop' => true])
                </div>
            </div>
        @endif
    </li>
@endforeach

</ul>

Good idea @sysmesh, but what about checking if data_types exists? If not exists i work with it like if it is an external link. Without using "__". I think this way is more user friendly.

@bedoz - You're probably right but I was aiming at a quick and dirty solution :-)
Of course this can be improved.

Ideally editing the add new menu item blade template to add a multiple checkbox selection of all roles (to allow them to see it) and then checking against that in the admin_menu blade to ensure permissions without even using the Laravel auth "can" method.

This issue has been automatically locked since there has not been any recent activity after it was closed. If you have further questions please ask in our Slack group.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TXRRNT picture TXRRNT  路  3Comments

iwasherefirst2 picture iwasherefirst2  路  3Comments

abacram picture abacram  路  3Comments

wislem picture wislem  路  3Comments

wp-src picture wp-src  路  3Comments