Allowing interviewees to edit their own content via the panel

Me again, thinking up solutions for user generated content.

TL;DR How can I give users access to a single page in the panel that matches an ID?

I’m rebuilding an online magazine I created called https://interfacelovers.com and the process to uploading the interview content was first a google doc with all the questions and then a drive folder with images and then someone had to move that to the kirby panel.

I’ve been thinking up a solution that would allow the interviewees to upload their content directly to the site. First I created a form on the frontend which works thanks to @texnixe and her support. Now I’m thinking about if it would be possible to create a user role for each interviewee that would only grant access to their specific interview page?

My idea is this:

  1. Create a new user (interviewee) with a specific role,

  2. Using hooks, automatically create a interview page which is directly linked to the newly created user.

  3. the user will be able to log in and only have access to their page in the panel and provide answer and images for all questions using the lovely panel fields.

I feel this is a better solution than the form because it would allow them to update content, come back later and format content in a more visual way.

Is something like this possible?

1 Like

One idea that I had was to place all the interview questions inside the user role blueprint but I wonder if that would be a good solution. Thoughts?

I wouldn’t create a new role per user, but one such role.

When the interview page is created (from a pages section within the user account set to max: 1), the user id of the user is stored in the interview page.

Interviewee.yml user role

title: Interviewee

permissions:
  # set permissions
sections:
  pages:
    type: pages
    template: interview
    parent: site.find('interviews')
    max: 1

In ther interview.yml blueprint, you set up the id field:

Title: Interview

fields:
  id: # id field that is auto-filled with id of user who creates page at page creation
    type: users
    disabled: true
    required: true
  question1:
    label: What are your favorite design tools?
    type: textarea

User id stored and disabled (email is shown but id stored):


The user role will only have access to the interview page (so in all other blueprints, the read option is set to false for this role.

The interview page gets a model where the isReadable() method is overridden, see here: https://getkirby.com/docs/cookbook/extensions/permission-tricks#override-the-isreadable-method-in-a-page-model

In this recipe, you will also find some other approaches.

1 Like

Thanks @texnixe for the super fast response.
This is an awesome way of doing it!

So in this solution, the user can create the interview page themselves, there’s no need to auto create the page?

Let me set this up now and give it a go.

Thanks again.

Yes, the user can create exactly one interview page in this setup. While you cannot prevent letting the user create a page title in the create dialog (this is only possible with a plugin at the moment), you can override the title they set in the interview page model, if you want:

In /site/models/interview.php

<?php

class InterviewPage extends Page {
    
    public static function create(array $props): Page
    {
        $props['content']['title'] = 'Interview '. $user->id();
        $props['slug'] = Str::slug('Interview '. $user->id()); // also change the slug
        return parent::create($props);
    }
}

Then set the changeTitle() option to false in the interview.yml blueprint.
The same can be done for the slug, if desired.

But this is totally optional.

This is the same model in which you will override the isReadable() method.

You probably want to disallow that the user role can publish the page but setting the changeStatus option to false as well, and probably some other tweaks.

1 Like

I’ve run into a bit of a problem with permissions @texnixe . I’ve disabled site access in the permissions and added the model but they user still can’t access that single page.

title: Interviewee

permissions:
  access:
    *: false
    panel: true
  site: false <- here!
  pages:
    *: false
    changeTitle: true
    create: true
    preview: true
    read: true
    update: true
  users: false
  user:
    changeRole: false
    delete: false
    
sections:
  interview:
    type: pages
    template: interview
    parent: site.find('interviews')
    max: 1 

Am I doing something wrong here?

I guess site access needs to be allowed.

What you can do, however, is create different site blueprints for the interviewee role and the other roles, as described in the cookbook recipe I linked above. That way, the user would only see what you let them see on the Dashboard, in this case, a pages section with this one interview page.

<?php

if(($user = kirby()->user()) && $user->role()->name() === 'interviewee') {
    $file = __DIR__ . '/blueprints/interviewee/site.yml';
} else {
    $file = __DIR__ . '/blueprints/site.yml';
}

Kirby::plugin('cookbook/role-blueprints', [
    'blueprints' => [
        'site' => $file
    ]
]);

Then move the site.yml from the site/blueprints folder into the given folder in the plugin.

On a sidenote, using the bouncer plugin mentioned in the recipe might be the quickest way to achieve what you want (not tested).

Yeah, I was just playing with the plugin but it seems you’re not able to create a page, you can only choose one.

cc @sylvainjule

Indeed, the bouncer works with a Pages field only, it currently cannot handle such a scenario.
It could though, by allowing access not with a field name but by searching for a page where the user id is selected in a given users field. You can open a ticket on GitHub but unfortunately I won’t be able to add the feature soon enough.

Currently the only way to achieve this with the plugin is a hook, something like:

'hooks' => [
    'user.create:after' => function ($user) {
        if($user->role() == 'interviewee') {
            $interviewsPage = site()->pages()->find('interviews');
            $interviewPage = $interviewsPage->createChild([
            	'content' => [
            		'title' => 'Interview '. $user->name()
            	],
            	'slug'      => Str::slug('Interview of '. $user->name()),
            	'template'  => 'interview'
            ]);

            if($interviewPage) {
            	$interviewPage->changeStatus('unlisted');
                $user->update(['canaccess' => $interviewPage->uri()]);
            }
        }
    }
]
1 Like

Hi @texnixe, I need to bump this back to the top because I seem to be having issues with this solution. Let me try and explain.

So the solution has been working fine for the past few years but now has started having issues when I allows interviewees to pick from a list of templates.

In the interviewee.yml user role file i have added different templates which they can pick when creating their page. The issue I am having is when someone choosing one of the new templates (designer or developer) their pages now shows up on all interviewees page.

title: Interviewee

permissions:
  access:
    *: false
    panel: true
    site: true
    account: true
  site: false
  languages: false
  pages:
    *: false
    changeTitle: true
    create: true
    read: true
    update: true
    preview: true
  users: false
  user:
    changeRole: false
    delete: false

sections:
  interview:
    type: pages
    template: 
      - designer
      - developer
      - interview
    parent: site.find('interviews')
    max: 1
    image:
      query: page.coverimage.toFile
      cover: true
      ratio: 1/1
      back: black

Do you have any idea whats going on here?

Are you using the bouncer plugin or which solution did you implement?

I am not using the bouncer public. I ended up using this solution you mentioned above here Going beyond Kirby’s permissions features | Kirby CMS.