permission management

This commit is contained in:
Etienne Roudeix
2013-10-22 15:04:06 +02:00
parent cb0619a7b3
commit c94fa9bc24
9 changed files with 278 additions and 186 deletions

View File

@@ -29,6 +29,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Command\ContainerAwareCommand;
use Thelia\Model\Admin;
use Thelia\Model\Map\ResourceI18nTableMap;
use Thelia\Model\Map\ResourceTableMap;
class GenerateResources extends ContainerAwareCommand
@@ -46,7 +47,7 @@ class GenerateResources extends ContainerAwareCommand
'output',
null,
InputOption::VALUE_OPTIONAL,
'Output format amid (string, sql)',
'Output format amid (string, sql, sql-i18n)',
null
)
;
@@ -55,7 +56,7 @@ class GenerateResources extends ContainerAwareCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
$class = new \ReflectionClass('Thelia\Core\Event\AdminResources');
$class = new \ReflectionClass('Thelia\Core\Security\Resource\AdminResources');
$constants = $class->getConstants();
@@ -69,12 +70,36 @@ class GenerateResources extends ContainerAwareCommand
$output->writeln(
'INSERT INTO ' . ResourceTableMap::TABLE_NAME . ' (`id`, `code`, `created_at`, `updated_at`) VALUES '
);
$compteur = 0;
foreach($constants as $constant => $value) {
if($constant == 'SUPERADMINISTRATOR') {
continue;
}
$compteur++;
$output->writeln(
"(NULL, '$value', NOW(), NOW())" . ($constant === key( array_slice( $constants, -1, 1, TRUE ) ) ? '' : ',')
"($compteur, '$value', NOW(), NOW())" . ($constant === key( array_slice( $constants, -1, 1, true ) ) ? ';' : ',')
);
}
break;
case 'sql-i18n':
$output->writeln(
'INSERT INTO ' . ResourceI18nTableMap::TABLE_NAME . ' (`id`, `locale`, `title`) VALUES '
);
$compteur = 0;
foreach($constants as $constant => $value) {
if($constant == 'SUPERADMINISTRATOR') {
continue;
}
$compteur++;
$title = ucwords( str_replace('.', ' / ', str_replace('admin.', '', $value) ) );
$output->writeln(
"($compteur, 'en_US', '$title'),"
);
$output->writeln(
"($compteur, 'fr_FR', '$title')" . ($constant === key( array_slice( $constants, -1, 1, true ) ) ? ';' : ',')
);
}
break;

View File

@@ -35,6 +35,7 @@
<loop class="Thelia\Core\Template\Loop\Product" name="product"/>
<loop class="Thelia\Core\Template\Loop\ProductSaleElements" name="product_sale_elements"/>
<loop class="Thelia\Core\Template\Loop\Profile" name="profile"/>
<loop class="Thelia\Core\Template\Loop\Resource" name="resource"/>
<loop class="Thelia\Core\Template\Loop\Feed" name="feed"/>
<loop class="Thelia\Core\Template\Loop\Title" name="title"/>
<loop class="Thelia\Core\Template\Loop\Lang" name="lang"/>

View File

@@ -128,7 +128,7 @@ class BaseAdminController extends BaseController
}
// Log the problem
$this->adminLogAppend("User is not granted for permissions %s", implode(", ", $permArr));
$this->adminLogAppend("User is not granted for resources %s with accesses %s", implode(", ", $resources), implode(", ", $accesses));
// Generate the proper response
$response = new Response();

View File

@@ -63,8 +63,9 @@ class AccessManager
$this->accessValue = $accessValue;
foreach($this->accessPows as $type => $value) {
if($accessValue >= $value) {
$accessValue -= $value;
$pow = pow(2, $value);
if($accessValue >= $pow) {
$accessValue -= $pow;
$this->accessGranted[$type] = true;
} else {
$this->accessGranted[$type] = false;

View File

@@ -25,6 +25,7 @@ namespace Thelia\Core\Template\Loop;
use Propel\Runtime\ActiveQuery\Criteria;
use Thelia\Core\Template\Element\BaseI18nLoop;
use Thelia\Core\Template\Element\BaseLoop;
use Thelia\Core\Template\Element\LoopResult;
use Thelia\Core\Template\Element\LoopResultRow;
@@ -44,7 +45,7 @@ use Thelia\Type\BooleanOrBothType;
* @package Thelia\Core\Template\Loop
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class Admin extends BaseI18nLoop
class Admin extends BaseLoop
{
public $timestampable = true;
@@ -83,17 +84,17 @@ class Admin extends BaseI18nLoop
$search->orderByFirstname(Criteria::ASC);
/* perform search */
$features = $this->search($search, $pagination);
$admins = $this->search($search, $pagination);
$loopResult = new LoopResult($features);
$loopResult = new LoopResult($admins);
foreach ($features as $feature) {
$loopResultRow = new LoopResultRow($loopResult, $feature, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("ID", $feature->getId())
->set("PROFILE",$feature->getProfileId())
->set("FIRSTNAME",$feature->getFirstname())
->set("LASTNAME",$feature->getLastname())
->set("LOGIN",$feature->getLogin())
foreach ($admins as $admin) {
$loopResultRow = new LoopResultRow($loopResult, $admin, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("ID", $admin->getId())
->set("PROFILE",$admin->getProfileId())
->set("FIRSTNAME",$admin->getFirstname())
->set("LASTNAME",$admin->getLastname())
->set("LOGIN",$admin->getLogin())
;
$loopResult->addRow($loopResultRow);

View File

@@ -79,20 +79,20 @@ class Profile extends BaseI18nLoop
$search->orderById(Criteria::ASC);
/* perform search */
$features = $this->search($search, $pagination);
$profiles = $this->search($search, $pagination);
$loopResult = new LoopResult($features);
$loopResult = new LoopResult($profiles);
foreach ($features as $feature) {
$loopResultRow = new LoopResultRow($loopResult, $feature, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("ID", $feature->getId())
->set("IS_TRANSLATED",$feature->getVirtualColumn('IS_TRANSLATED'))
foreach ($profiles as $profile) {
$loopResultRow = new LoopResultRow($loopResult, $profile, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("ID", $profile->getId())
->set("IS_TRANSLATED",$profile->getVirtualColumn('IS_TRANSLATED'))
->set("LOCALE",$locale)
->set("CODE",$feature->getCode())
->set("TITLE",$feature->getVirtualColumn('i18n_TITLE'))
->set("CHAPO", $feature->getVirtualColumn('i18n_CHAPO'))
->set("DESCRIPTION", $feature->getVirtualColumn('i18n_DESCRIPTION'))
->set("POSTSCRIPTUM", $feature->getVirtualColumn('i18n_POSTSCRIPTUM'))
->set("CODE",$profile->getCode())
->set("TITLE",$profile->getVirtualColumn('i18n_TITLE'))
->set("CHAPO", $profile->getVirtualColumn('i18n_CHAPO'))
->set("DESCRIPTION", $profile->getVirtualColumn('i18n_DESCRIPTION'))
->set("POSTSCRIPTUM", $profile->getVirtualColumn('i18n_POSTSCRIPTUM'))
;
$loopResult->addRow($loopResultRow);

View File

@@ -0,0 +1,115 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Template\Loop;
use Propel\Runtime\ActiveQuery\Criteria;
use Thelia\Core\Security\AccessManager;
use Thelia\Core\Template\Element\BaseI18nLoop;
use Thelia\Core\Template\Element\LoopResult;
use Thelia\Core\Template\Element\LoopResultRow;
use Thelia\Core\Template\Loop\Argument\ArgumentCollection;
use Thelia\Core\Template\Loop\Argument\Argument;
use Thelia\Model\ResourceQuery;
use Thelia\Type;
use Thelia\Type\BooleanOrBothType;
/**
*
* Resource loop
*
*
* Class Resource
* @package Thelia\Core\Template\Loop
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class Resource extends BaseI18nLoop
{
public $timestampable = true;
/**
* @return ArgumentCollection
*/
protected function getArgDefinitions()
{
return new ArgumentCollection(
Argument::createIntTypeArgument('profile')
);
}
/**
* @param $pagination
*
* @return \Thelia\Core\Template\Element\LoopResult
*/
public function exec(&$pagination)
{
$search = ResourceQuery::create();
/* manage translations */
$locale = $this->configureI18nProcessing($search);
$profile = $this->getProfile();
if (null !== $profile) {
$search->leftJoinProfileResource('profile_resource')
->withColumn('profile_resource.access', 'access');
//$search->filterById($id, Criteria::IN);
}
$search->orderById(Criteria::ASC);
/* perform search */
$resources = $this->search($search, $pagination);
$loopResult = new LoopResult($resources);
foreach ($resources as $resource) {
$loopResultRow = new LoopResultRow($loopResult, $resource, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("ID", $resource->getId())
->set("IS_TRANSLATED",$resource->getVirtualColumn('IS_TRANSLATED'))
->set("LOCALE",$locale)
->set("CODE",$resource->getCode())
->set("TITLE",$resource->getVirtualColumn('i18n_TITLE'))
->set("CHAPO", $resource->getVirtualColumn('i18n_CHAPO'))
->set("DESCRIPTION", $resource->getVirtualColumn('i18n_DESCRIPTION'))
->set("POSTSCRIPTUM", $resource->getVirtualColumn('i18n_POSTSCRIPTUM'))
;
if (null !== $profile) {
$accessValue = $resource->getVirtualColumn('access');
$manager = new AccessManager($accessValue);
$loopResultRow->set("VIEWABLE", $manager->can(AccessManager::VIEW))
->set("CREATABLE", $manager->can(AccessManager::CREATE))
->set("UPDATABLE", $manager->can(AccessManager::UPDATE))
->set("DELETABLE", $manager->can(AccessManager::DELETE));
}
$loopResult->addRow($loopResultRow);
}
return $loopResult;
}
}

View File

@@ -1193,7 +1193,6 @@ INSERT INTO `order_status_i18n` (`id`, `locale`, `title`, `description`, `chapo`
(5, 'en_US', 'Canceled', '', '', ''),
(5, 'fr_FR', 'Annulée', '', '', '');
/**
generated with command : php Thelia thelia:generate-resources --output sql
*/
@@ -1220,4 +1219,55 @@ INSERT INTO resource (`id`, `code`, `created_at`, `updated_at`) VALUES
(NULL, 'admin.configuration.profile', NOW(), NOW()),
(NULL, 'admin.configuration.shipping-zone', NOW(), NOW()),
(NULL, 'admin.configuration.tax', NOW(), NOW()),
(NULL, 'admin.configuration.template', NOW(), NOW())
(NULL, 'admin.configuration.template', NOW(), NOW());
/**
generated with command : php Thelia thelia:generate-resources --output sql-i18n
*/
INSERT INTO resource_i18n (`id`, `locale`, `title`) VALUES
(1, 'en_US', 'Address'),
(1, 'fr_FR', 'Address'),
(2, 'en_US', 'Configuration / Admin'),
(2, 'fr_FR', 'Configuration / Admin'),
(3, 'en_US', 'Configuration / Area'),
(3, 'fr_FR', 'Configuration / Area'),
(4, 'en_US', 'Configuration / Attribute'),
(4, 'fr_FR', 'Configuration / Attribute'),
(5, 'en_US', 'Category'),
(5, 'fr_FR', 'Category'),
(6, 'en_US', 'Configuration'),
(6, 'fr_FR', 'Configuration'),
(7, 'en_US', 'Content'),
(7, 'fr_FR', 'Content'),
(8, 'en_US', 'Configuration / Country'),
(8, 'fr_FR', 'Configuration / Country'),
(9, 'en_US', 'Coupon'),
(9, 'fr_FR', 'Coupon'),
(10, 'en_US', 'Configuration / Currency'),
(10, 'fr_FR', 'Configuration / Currency'),
(11, 'en_US', 'Customer'),
(11, 'fr_FR', 'Customer'),
(12, 'en_US', 'Configuration / Feature'),
(12, 'fr_FR', 'Configuration / Feature'),
(13, 'en_US', 'Folder'),
(13, 'fr_FR', 'Folder'),
(14, 'en_US', 'Configuration / Language'),
(14, 'fr_FR', 'Configuration / Language'),
(15, 'en_US', 'Configuration / Mailing-system'),
(15, 'fr_FR', 'Configuration / Mailing-system'),
(16, 'en_US', 'Configuration / Message'),
(16, 'fr_FR', 'Configuration / Message'),
(17, 'en_US', 'Configuration / Module'),
(17, 'fr_FR', 'Configuration / Module'),
(18, 'en_US', 'Order'),
(18, 'fr_FR', 'Order'),
(19, 'en_US', 'Product'),
(19, 'fr_FR', 'Product'),
(20, 'en_US', 'Configuration / Profile'),
(20, 'fr_FR', 'Configuration / Profile'),
(21, 'en_US', 'Configuration / Shipping-zone'),
(21, 'fr_FR', 'Configuration / Shipping-zone'),
(22, 'en_US', 'Configuration / Tax'),
(22, 'fr_FR', 'Configuration / Tax'),
(23, 'en_US', 'Configuration / Template'),
(23, 'fr_FR', 'Configuration / Template');

View File

@@ -118,111 +118,60 @@
<div class="tab-pane fade {if $oder_tab == 'permissions'}active in{/if}" id="permissions">
<div class="col-md-12 title title-without-tabs">
{intl l="Manage permissions"}
</div>
{*
<div class="form-group">
<label for="" class="label-control">{intl l="Choose a country"} :</label>
<form id="country-selector-form" action="{url path="/admin/configuration/profiles/update/$profile_id"}" method="GET">
<input type="hidden" name="tab" value="taxes">
<select id="country-selector" name="country" data-toggle="selectpicker">
{loop type="country" name="country-list"}
<option value="{$ID}" {if $ID == $asked_country}selected="selected"{/if}>{$TITLE}</option>
{/loop}
</select>
</form>
</div>
<p><strong>{intl l="Countries that have the same profile"} :<strong></p>
<p class="lead js-collapse" id="same_countries" data-collapse-height="86">
{$matchedCountries.first=$asked_country}
{loop type="profile-country" name="same-country-list" profile=$ID ask="countries" country=$asked_country}
{$matchedCountries[]=$COUNTRY}
<span class="label label-info">{$COUNTRY_TITLE}</span>
{/loop}
{elseloop rel="same-country-list"}
<span class="label label-danger">{intl l="NONE"}</span>
{/elseloop}
</p>
<button data-collapse-block="same_countries" type="button" class="btn btn-info btn-sm btn-block js-collapse-btn" style="margin-bottom: 15px">See all countries</button>
<div class="row">
<div class="col-md-6">
<div id="panel" class="panel panel-default place">
<div class="panel-heading">
<h3 class="panel-title">{intl l="Manage the profile taxes appliance order"}</h3>
</div>
<div class="panel-body">
{assign lastPosition 0}
{loop type="profile-country" name="existing-tax-list" profile=$ID country=$asked_country}
{if $POSITION != $lastPosition}
{assign lastPosition $POSITION}
{if $LOOP_COUNT > 1}
</div>
{/if}
<div class="drop-group droppable add-to-group">
<p class="drop-message">
<span class="glyphicon glyphicon-plus"></span>
<span class="message">{intl l="Add tax to this group"}</span>
</p>
{/if}
<div class="drag" data-id="{$TAX}">{$TAX_TITLE}</div>
{if $LOOP_COUNT == $LOOP_TOTAL}
</div>
{/if}
{/loop}
{elseloop rel="existing-tax-list"}
<div class="drop-group droppable add-to-group">
<p class="drop-message">
<span class="glyphicon glyphicon-plus"></span>
<span class="message">{intl l="Add tax to this group"}</span>
</p>
<table class="table table-striped table-condensed table-left-aligned">
<caption>
{intl l="Manage permissions"}
</caption>
<thead>
<tr>
<th rowspan="2">{intl l="Resource"}</th>
<th rowspan="2">{intl l="Title"}</th>
<th colspan="4" class="text-center">{intl l="Rights"}</th>
</tr>
<tr>
<th>{intl l="View"}</th>
<th>{intl l="Create"}</th>
<th>{intl l="Update"}</th>
<th>{intl l="Delete"}</th>
</tr>
</thead>
<tbody>
{loop type="resource" name="resource-list" profile=$ID backend_context="1"}
<tr>
<td>{$CODE}</td>
<td>{$TITLE}</td>
<td>
<div class="make-switch switch-mini" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input type="checkbox" {if $VIEWABLE == 1}checked="checked"{/if}>
</div>
</td>
<td>
<div class="make-switch switch-mini" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input type="checkbox" {if $CREATABLE == 1}checked="checked"{/if}>
</div>
</td>
<td>
<div class="make-switch switch-mini" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input type="checkbox" {if $UPDATABLE == 1}checked="checked"{/if}>
</div>
</td>
<td>
<div class="make-switch switch-mini" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input type="checkbox" {if $DELETABLE == 1}checked="checked"{/if}>
</div>
</td>
</tr>
{/loop}
</tbody>
<tfoot>
<tr>
<td colspan="3">
<button type="submit" class="btn btn-default btn-primary pull-right"><span class="glyphicon glyphicon-check"></span> {intl l="Save"}</button>
</td>
</tr>
</tfoot>
</table>
{/elseloop}
</div>
<div class="panel-footer droppable create-group">
<p class="drop-message">
<span class="glyphicon glyphicon-plus"></span>
<span class="message">{intl l="Drop tax here to create a tax group"}</span>
</p>
</div>
</div>
<a href="#tax_list_update_dialog" data-toggle="modal" id="apply-taxes-rules" class="btn btn-default btn-primary btn-block"><span class="glyphicon glyphicon-check"></span> {intl l="Apply"}</a>
</div>
<div class="col-md-6">
<div id="panel-list" class="panel panel-default take">
<div class="panel-heading">
<h3 class="panel-title">Available taxes</h3>
</div>
<div class="panel-body">
{loop type="tax" name="tax-list" exclude_profile=$ID country=$asked_country}
<div class="draggable" data-id="{$ID}">{$TITLE}</div>
{/loop}
</div>
<div class="panel-footer droppable remove-from-group">
<p class="drop-message">
<span class="glyphicon glyphicon-minus"></span>
<span class="message">{intl l="Drop tax here to delete from group"}</span>
</p>
</div>
</div>
</div>
*}
</div>
</div>
@@ -235,60 +184,6 @@
</div>
{* Confirmation dialog *}
{*form name="thelia.admin.profile.taxlistupdate"}
{if $form_error_message}
{$taxUpdateError = true}
{else}
{$taxUpdateError = false}
{/if}
{* Capture the dialog body, to pass it to the generic dialog *}
{*capture "tax_list_update_dialog"}
<input type="hidden" name="profile_id" value="{$profile_id}">
<input type="hidden" name="tab" value="taxes">
{form_hidden_fields form=$form}
{form_field form=$form field='country_list'}
<p>{intl l="Profile taxes will be update for the following countries :"}</p>
<div class="form-group">
<div class="input-group">
<select id="countries-select" class="" name="{$name}" data-toggle="selectpicker" multiple>
{loop type="country" name="country-list"}
<option value='{$ID}' {if (!$value AND in_array($ID, $matchedCountries)) OR ($value AND in_array($ID, $value))}selected="selected"{/if}>{$TITLE}</option>
{/loop}
</select>
<span class="input-group-btn">
<button class="btn btn-primary js-uncheck-all" data-uncheck-select="countries-select">{intl l="uncheck all"}</button>
</span>
</div>
</div>
{/form_field}
{/capture}
{include
file = "includes/generic-create-dialog.html"
dialog_id = "tax_list_update_dialog"
dialog_title = {intl l="Update profile taxes"}
dialog_body = {$smarty.capture.tax_list_update_dialog nofilter}
dialog_ok_label = {intl l="Edit profile taxes"}
dialog_cancel_label = {intl l="Cancel"}
form_action = {url path="/admin/configuration/profiles/saveTaxes"}
form_enctype = {form_enctype form=$form}
form_error_message = $form_error_message
}
{/form*}
{/block}
{block name="javascript-initialization"}
@@ -297,6 +192,10 @@
<script src='{$asset_url}'></script>
{/javascripts}
{javascripts file='assets/js/bootstrap-switch/bootstrap-switch.js'}
<script src="{$asset_url}"></script>
{/javascripts}
{javascripts file='assets/js/main.js'}
<script src='{$asset_url}'></script>
{/javascripts}