Winter CMS resources and help articles

Simple and to the point. Optimized by the community.

Control backend access to static pages on a per-page basis

4
by LukeTowers, last modified on January 8th, 2022

Sometimes you may want to control the backend access to Winter.Pages static pages on a per-page basis; this trick should give you a starting point to achieve the specific goals of your project.

Note: In its current implementation this is not a perfect solution, for instance if you want to give access to a page that's a child of another page then the user will need access to the parent page as well in order for this to work.

<?php 

namespace MyAuthor\MyPlugin;

use App;
use Event;
use BackendAuth;
use ApplicationException;
use Cms\Classes\Theme;
use Backend\Controllers\Users;
use Backend\Models\User;
use Backend\Models\UserPreference;
use Winter\Pages\Classes\Page;
use Winter\Pages\Controllers\Index;

use System\Classes\PluginBase;

class Plugin extends PluginBase
{
    protected $userPreferenceKey = 'myauthor::content.allowed_static_pages';

    public function boot()
    {
        $this->extendBackendUsersController();
        $this->extendBackendUserModel();
        $this->extendWinterPagesPageModel();
        $this->extendWinterPagesIndexController();
    }

    /**
     * Extends the Backend Users controller to add the field that will be used
     * to specify specific pages that the user has access to.
     *
     * @return void
     */
    protected function extendBackendUsersController()
    {
        // Setup specific page permission functionality
        // @TODO: Indicate parent / child page relationships in page picker
        Users::extendFormFields(function ($form, $model, $context) {
            if (!in_array($context, ['update'])) {
                return;
            }

            $form->addTabFields([
                'allowed_static_pages' => [
                    'tab' => 'backend::lang.user.permissions',
                    'label' => 'Allowed Static Pages',
                    'comment' => 'The static pages that the user is allowed to edit',
                    'type' => 'checkboxlist',
                    'options' => Page::listInTheme(Theme::getActiveTheme())->pluck('title', 'fileName')->toArray(),
                ],
            ]);
        });
    }

    /**
     * Extends the Backend User model to make the content permissions data available as a
     * dynamic attribute that stores the data as a UserPreference record
     *
     * @return void
     */
    protected function extendBackendUserModel()
    {
        User::extend(function ($model) {
            $model->addDynamicMethod('getAllowedStaticPagesAttribute', function () use ($model) {
                return UserPreference::forUser($model)->get($this->userPreferenceKey);
            });

            // TODO: Warning, users can set this value themselves by including it in the POST data sent by an
            // update request on their myaccount page, this is why there is an explicit isSuperuser() check here
            $model->addDynamicMethod('setAllowedStaticPagesAttribute', function ($value) use ($model) {
                if ($model->exists && BackendAuth::user()->isSuperuser()) {
                    UserPreference::forUser($model)->set($this->userPreferenceKey, $value);
                }
            });
        });
    }

    /**
     * Extends the Winter.Pages Page model to prevent users from deleting or persisting changes to
     * pages that they don't have access to.
     *
     * @return void
     */
    protected function extendWinterPagesPageModel()
    {
        Page::extend(function ($model) {
            if (!App::runningInBackend()) {
                return;
            }

            $allowedPages = UserPreference::forUser()->get($this->userPreferenceKey);
            if (empty($allowedPages)) {
                return;
            }

            $model->bindEvent('model.beforeDelete', function () {
                throw new ApplicationException("You do not have permission to delete pages.");
            });

            $model->bindEvent('model.beforeSave', function () use ($model, $allowedPages) {
                if (!$model->exists) {
                    throw new ApplicationException("You do not have permission to create new pages.");
                }
                if (!in_array($model->fileName, $allowedPages)) {
                    throw new ApplicationException("You are not permitted to modify {$model->fileName}");
                }
            });
        });
    }

    /**
     * Extends the Winter.Pages Index controller to only allow pages that the user has
     * access to to be loaded.
     *
     * @return void
     */
    protected function extendWinterPagesIndexController()
    {
        Index::extend(function ($controller) {
            $allowedPages = UserPreference::forUser()->get($this->userPreferenceKey);
            if (empty($allowedPages)) {
                return;
            }

            Event::listen('cms.object.listInTheme', function ($object, $list) use ($allowedPages) {
                if (!($object instanceof Page)) {
                    return;
                }

                foreach ($list as $index => $page) {
                    if (!in_array($page->fileName, $allowedPages)) {
                        $list->forget($index);
                    }
                }
            });
        });
    }
}

Discussion

0 comments

We use cookies to measure the performance of this website. Do you want to accept these cookies?