Adding tabs only for specific role (editor, member,admin) and specfic fields (ex: jobs) of members?

Hi,

Just to be sure, i have three type of role, the classical : member, editor and admin role

Each member/editor have the same user profile page editable with tab section :

  • about
  • research
  • cv
  • etc.

Based on that, i have two question :

  • First, I’m interested to add a new /meta tab with specific fields, only available for Editor, the one blueprint by role don’t work here. I need to use programmable blueprint ?
  • Secondly, i’m interested to separate tab by Role but also by another field, for example “Jobs” with different category of tab.

So i could have :

  • An editor “Musk”, with “Jobs” === “CEO” that access :
    • about
    • sales
    • cv
    • meta
  • A member “Paul”, with “Jobs” === “Engineer” that access :
    • about
    • git project
  • An editor “Sylvie”, with “Jobs” === “Engineer” that access :
    • about
    • git project
    • blog articles
    • meta

What’s the best combination to do this job, i need to use programmable blueprint for both filtering/injecting “tabs” by user fields “Jobs:” name AND by roles ?

Thanks for your help

Hi @reyman,

1. Tab only for editors

You don’t need programmable blueprints for this. Kirby loads the user blueprint by role (site/blueprints/users/.yml, falling back to default.yml). So just add the meta tab to editor.yml and leave it out of member.yml. To avoid maintaining the same tabs several times, you can move them into reusable tab files in site/blueprints/tabs/:

# site/blueprints/users/editor.yml
title: Editor
tabs:
  about: tabs/user-about
  cv: tabs/user-cv
  meta: tabs/user-meta
# site/blueprints/users/member.yml
title: Member
tabs:
  about: tabs/user-about
  cv: tabs/user-cv

2.Tabs depending on a field like “Jobs”

when works for fields and sections, but not for tabs. So you have these options:

  • when on the sections/fields inside the tab. Simple, but the tab itself stays visible (just empty).
  • A custom user model that overrides blueprint(). This is what I’d do. Unlike a programmable blueprint, the model knows which user is being edited ($this), so it can read the “Jobs” field and remove tabs:
<?php
// site/plugins/user-tabs/index.php

use Kirby\Cms\User;
use Kirby\Cms\UserBlueprint;

class EditorUser extends User
{
    /**
     * Shows the meta tab only for editors whose jobs include "ceo"
     */
    public function blueprint(): UserBlueprint
    {
        if ($this->blueprint !== null) {
            return $this->blueprint;
        }

        $props = UserBlueprint::load('users/editor');
        $jobs  = $this->content()->get('jobs')->split();

        if (in_array('ceo', $jobs) === false) {
            unset($props['tabs']['meta']);
        }

        return $this->blueprint = new UserBlueprint([...$props, 'model' => $this]);
    }
}

Kirby::plugin('your/user-tabs', [
    'userModels' => [
        'editor' => EditorUser::class
    ]
]);

I tested it with Kirby 5.6.0.

You need one model per role that should behave this way.

  • Programmable blueprint via the blueprints plugin option. I wouldn’t use it here. The closure only receives $kirby, not the model being edited (see Blueprint::find()). kirby()->user() is the logged-in user, not the one whose profile is open. On top of that, the result is cached per blueprint name for the rest of the request.
  • Make “Jobs” a role. This only makes sense if the jobs need different permissions anyway. A user has exactly one role, and permissions are attached to it, so every combination (editor-ceo, member-engineer, …) becomes its own role with its own set of permissions.

Also keep in mind that hiding a tab is only a UI measure, not access control. Role permissions work per action (e.g. user.update), not per field. To actually stop users from changing certain fields, you’d need a hook such as user.update:before that rejects the change.

Best

Hi,

Thanks for your long answer !

About the part 2. i will test using this solution, thanks !

About the part 1. i already test, but it only display the /meta tab for Editor user profile, but my needs is different, i will want that Editor user have the capacity to see and to edit the /meta information tab for all other users/members.

Hi,

I see. Then the condition isn’t the role of the edited user but of the logged-in one, and for that kirby()->user() is exactly right.

Use the model from part 2 for every role and add the meta tab when the viewer is an editor or admin:

<?php
// site/plugins/user-tabs/index.php

use Kirby\Cms\User;
use Kirby\Cms\UserBlueprint;
use Kirby\Exception\PermissionException;

class ProfileUser extends User
{
    /**
     * Adds the meta tab to every profile when the logged-in user is an editor or admin
     */
    public function blueprint(): UserBlueprint
    {
        if ($this->blueprint !== null) {
            return $this->blueprint;
        }

        $props      = UserBlueprint::load('users/' . $this->role()->id());
        $viewerRole = $this->kirby()->user()?->role()->id();

        if (in_array($viewerRole, ['editor', 'admin'], true) === true) {
            $props['tabs']['meta'] = 'tabs/user-meta';
        }

        return $this->blueprint = new UserBlueprint([...$props, 'model' => $this]);
    }
}

Kirby::plugin('your/user-tabs', [
    'userModels' => [
        'admin'  => ProfileUser::class,
        'editor' => ProfileUser::class,
        'member' => ProfileUser::class,
    ],
    'hooks' => [
        // Hiding the tab is not enough: a member could still set the field via the API.
        // $values holds the whole form, so compare old and new value.
        'user.update:before' => function (User $user, array $values) {
            $viewerRole = $this->user()?->role()->id();
            $newNote    = array_change_key_case($values)['internalnote'] ?? '';
            $oldNote    = $user->content()->get('internalNote')->value() ?? '';

            if ($newNote !== $oldNote && in_array($viewerRole, ['editor', 'admin'], true) === false) {
                throw new PermissionException(message: 'Only editors can change the meta information');
            }
        },
    ],
]);

Remove meta from editor.yml again, the model adds it now.

By default every role may edit other users. If members shouldn’t, add this to member.yml:

permissions:
  users:
    update: false

To combine this with part 2, put both conditions into the same blueprint(): $this->content() for the edited user’s jobs, $this->kirby()->user() for the viewer’s role.

Best