Creating Permissions

Improvements?

Let us know by posting here.

Creating a Task Permission

To create a custom task permission in a package, add a permission key during installation like this:

public function install()
{
    $pkg = parent::install();
    \Concrete\Core\Permission\Key\Key::add('admin', 'change_theme_settings', 'Change Theme Settings', '', false, false, $pkg);
}
  • 'admin': Permission category handle.
  • 'change_theme_settings': Permission key handle.
  • 'Change Theme Settings': Name of the permission.
  • Description and two boolean parameters for workflow trigger and custom class use.
  • $pkg: Package object.

This permission will be available on the Task Permissions Dashboard page.

Creating a Page, File, or User Permission

For a permission related to existing objects like pages, change the category handle:

public function install()
{
    $pkg = parent::install();
    \Concrete\Core\Permission\Key\Key::add('page', 'change_custom_theme_settings', 'Change Theme Settings', '', false, false, $pkg);
}

Here, 'page' is used as the permission category handle, defining it as a custom page permission.

Custom Access Entity in Concrete CMS

Concrete CMS provides various access entities like Group, User, and Page Owner. Developers can easily create custom access entities for more tailored permission settings.

eCommerce Scenario

For an eCommerce site, you might need a "Product Purchaser" Access Entity. This entity allows specific access based on product purchase, beyond what is possible with standard user groups.

Installing the Access Entity

Install the entity by updating the database. Add the following in your controller's install() method:

use \Concrete\Core\Permission\Access\Entity\Type\Type;
use \Concrete\Core\Permission\Category;

$type = Type::getByHandle('product_purchaser');
if (!is_object($type)) {
    $type = Type::add('product_purchaser', 'Product Purchaser', $pkg);
}
$category = Category::getByHandle('page');
$category->associateAccessEntityType($type);

This code registers a "Product Purchaser" entity for page-related permissions.

Backend Implementation

Create ProductPurchaserEntity.php in packages/super_store/src/Concrete/Permission/Access/Entity/ with necessary functions like getAccessEntityUsers(), getAccessEntityTypeLinkHTML(), and others for handling user access based on product purchases.

Frontend Code

Add frontend interaction in packages/super_store/elements/permission/access/entity/types/product_purchaser.php. This involves JavaScript for selecting products and updating the permissions interface.

Routing

Set up routing in your package controller’s on_start():

\Route::register(
    '/tools/packages/super_store/permissions/access/entity/types/product_purchaser',
    '\Concrete\Package\SuperStore\Controller\Permissions\Access\Entity\Types\ProductPurchaser::process'
);

Then create ProductPurchaser.php in packages/super_store/controllers/permissions/access/entity/types/ for handling AJAX requests and returning JSON data for the entity.

This custom entity allows locking down site areas based on specific product purchases, providing a flexible permission system for eCommerce scenarios.

Creating a Permissions Category

For custom eCommerce solutions like \SuperStore\Product\Product, adding unique permissions such as "View Product", "Edit Product", and "Delete Product" necessitates a new permission category. These permissions apply specifically to Product objects, allowing control over who can edit certain products. This requires both backend and frontend custom code within a Concrete package.

Creating the Custom Category Object

Prepare Controller

Add autoload configuration to controller.php for the package:

$pkgAutoloaderMapCoreExtensions = true;

Create the Assignment Database Table

Create a table for Product-Permission assignments:

CREATE TABLE `SuperStoreProductAssignments` (
  `productID` int(10) unsigned NOT NULL DEFAULT '0',
  `pkID` int(10) unsigned NOT NULL DEFAULT '0',
  `paID` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`cID`,`pkID`,`paID`),
  KEY `paID` (`paID`,`pkID`),
  KEY `pkID` (`pkID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Concrete CMS package database documentation.

Install Permission Category and Keys

In install() method of the controller, set up the new category and permissions:

use Concrete\Core\Permission\Category\Category;
use Concrete\Core\Permission\Key\Key;
use Concrete\Core\Permission\Access\Entity\Type as AccessEntityType;

public function install() {
    $pkg = parent::install();
    // Category setup
    $category = Category::getByHandle('product');
    if (!is_object($category)) {
        $category = Category::add('product', $pkg);
        $category->associateAccessEntityType(AccessEntityType::getByHandle('group'));
        $category->associateAccessEntityType(AccessEntityType::getByHandle('user'));
    }

    // Permission keys setup
    // 'view_product', 'edit_product', 'delete_product'
}

Implement Concrete\Core\Permission\ObjectInterface

Ensure Products class implements the permissions object interface:

namespace SuperStore\Product
use Concrete\Core\Permission\ObjectInterface;

class Products implements ObjectInterface {
    // Implementation of the interface methods
}

Create the Classes

Create the required classes for custom permission:

  • Key: ProductKey extends Key
  • Access: ProductAccess extends Access
  • Access List Item: ProductListItem extends ListItem
  • Response: ProductResponse extends Response
  • Assignment: Implement custom logic in ProductAssignment extends Assignment

Handle custom logic in ProductAssignment for querying SuperStoreProductAssignments and managing permissions.

Creating the Category User Interface

To set new permissions for the Product object, create a Dashboard page. Follow Single Pages for guidance.

Create a Dashboard Page

Create a Dashboard Page in the Package controller. Example path:

/dashboard/super_store/product/permissions

This page loads a product by its ID:

http://www.example.com/dashboard/super_store/products/permissions/120

view() method in Dashboard controller:

public function view($productID = null)
{
    $product = \SuperStore\Product\Product::getByID($productID);
    $this->set('product', $product);
}

Dashboard view form:

<form method="post" action="<?=$view->action('save_permissions')?>">
    <input type="hidden" name="productID" value="<?=$product->getProductID()?>">
    <?=$token->output('save_permissions')?>
    <fieldset>
        <div id="ccm-permission-list-form">
            <?php View::packageElement('permission/lists/product', 'super_store', array(
                "product" => $product)); ?>
        </div>
    </fieldset>
    <div class="form-actions">
        <button type="submit" name="submit" class="btn pull-right btn-primary"><?=t('Save Permissions')?></button>
    </div>
</form>

Package element packages/super_store/elements/permission/lists/product.php:

<table class="ccm-permission-grid table table-striped">
    <!-- Permission rows -->
</table>
<script type="text/javascript">
    ccm_permissionLaunchDialog = function(link) {
        // JavaScript for dialog
    }
</script>
  1. Create a table with permission keys.
  2. Loop through permission keys, setting the product object.
  3. Display permissions and access data.
  4. Include custom JavaScript for dialog interaction.

Define Route

Define a route in the package controller for permission dialogs:

public function on_start()
{
    \Route::register(
        Router::route(array('/permissions/dialogs/product', 'super_store')),
        '\Concrete\Package\SuperStore\Controller\Permissions\ProductDialog::view'
    );
}

Create ProductDialog controller:

namespace Concrete\Package\SuperStore\Controller\Permissions;

class ProductDialog extends BackendInterfaceController
{
    protected $viewPath = '/dialogs/permissions/product_dialog';

    public function canAccess() {
        // Access control logic
    }
    public function view() {
        // View logic
    }
}

Include permission/details/product.php in the view.

Backend Permission Category Processor

Define a custom route for the automatically generated tools URL:

\Route::register('/tools/packages/super_store/permissions/categories/product',
    '\Concrete\Package\SuperStore\Controller\Permissions::process'
);

Create Permissions controller:

namespace Concrete\Package\SuperStore\Controller;

class Permissions extends \Concrete\Core\Controller\Controller
{
    public function process() {
        // Processing logic
    }
}

Using the Permissions

Use custom permissions with:

$product = Product::getByID($productID);
$p = new Permissions($product);
if ($p->canViewProduct()) {
    // Can view!
}
if ($p->canEditProduct()) {
    // Can edit!
}
if ($p->canDeleteProduct()) {
    // Can delete!
}

Expand product permissions over time, supporting all Concrete access entities and duration-based permissions.