11.3 - Criação do plugin cake-acl-br
Não importa se seu sonho vai se realizar hoje ou amanhã, mas sim que você trabalhe para o alcançar todos os dias.
O plugin cake-acl-br foi criado vindo de outros 3 plugins.
Primeito criei o twbs-cake-css - https://github.com/ribafs/twbs-cake-css, depois criei o cake-control, que já removi do GitHub, depois o cake-control-br - https://github.com/ribafs/cake-control-br e finalmente este, o cake-acl-br, que já está no release 1.30, ou seja, já o atualizei 30 vezes.
Seus recursos principais são:
• Menu de topo com o element topmenu
• Uso do framework Bootstrap
• Busca com paginação
• Senhas criptografadas com Bcrypt
• Controle de Acesso tipo ACL com administração web
• Dois Layouts: admin e default com cor de fundo que os diferencia
• Datas formatadas para pt-br (veja em Customers)
• Tradução do template do Bake para pt-br
• Customização do bootstrap_cli adicionando os campos login e logout na geração do Bake
• Validação via frontend no login com pattern e minlenght, para exigir senha forte, com pelo menos 8 dígitos, uma maiúscula, uma minúscula e um símbolo. Também com recomendações para validação semelhante pelo CakePHP no
UsersTable.php
Geralmente quando vou criar um novo aplicativo eu uso o cake-acl-br, pois o considero muito útil.
Vou esquecer a criação dos plugins do qual ele deriva e me focar apenas na criação do cake-acl-br – https://github.com/ribafs/cake-acl-br.
Um pouco de como implementei os recursos
- Framework bootstrap juntamente com o template do bake para implementar o Bootstrap no código, especialmente nas views. Estes recursos vieram do plugin https://github.com/elboletaire/twbs-cake-plugin
- Busca com Paginação. Este veio do Tayron:
http://www.tayron.com.br/blog/121/criando-um-formulario-de-pesquisa-com-cakephp3. O código para implantar a busca encontra-se no plugin, mas comentado. Para habilitar basta descomentar no action e na view.
- Depois resolvi traduzir as strings do template do bake para que ele gerasse o código traduzido. Esta tradução encontra-se basicamente nos arquivos da pasta src/Template/Bake
- Também adicionei no docs/bootstrap_cli.php código para que ao gerar o código com bake já seja gerado o código para login e logout
- Criei um element (topmenu) com um menu de topo e já o incorporei ao plugin em src/Template/Element/topmenu.ctp
- Implementei a criptografia com Bcrypt e incorporei ao plugin em src/Template/Bake/Model/entity.ctp
- Criei um componente para controlar o acesso do aplicativo, que faz o principal trabalho de ACL (ControlComponent) e inseri em
src/Controller/Component/ControlComponent.php
- Criei um segundo layout, o primeiro para usuários administradores e o outro para os dois restantes e adicionei em
src/Template/Layout
- Adicionei ao código do componente ControlComponent suporte para MySQL e PostgreSQL e já ofereci dois scripts .sql para cada um
- Criei um tratamento de erro para o PDO em docs/pdo_error.ctp e deve ser copiado para o aplicativo/src/Tempalte/Error
- Criei um pequeno estilo em CSS para ajustes no CSS do BootStrap, que está em docs/custom.css e deve ser copiado para webroot/css
- A parte mais trabalhosa foi encontrar o código correto para lidar com o componente Auth no AppController. Neste controller tem muito código importante para o plugin.
- Finalmente adicionei validação ao login. No frontend com HTML5 e no backend com validação no model, de forma que exija senhas com pelo menos um caratere maiúsculo, um minúsculo, um número, um símbolo e 8 dígitos.
Começando pra valer
Irei começar do plugin cake-control-br, esquecendo que iniciei com o twbs-cake-plugin.
Fazendo o download do cake-acl-br:
https://github.com/ribafs/cake-acl-br
Não vou mostrar detalhes do php na construção do componente, mas apenas detalhes do CakePHP na criação do plugin, que considero relevantes.
Detalhes
https://github.com/ribafs/cake-acl-br
Observação importante
O pluigin cake-acl-br
foi descontinuado e admin-br continua de onde cake-acl-br parou.
A
estrutura de diretórios ficou assim:
/config
bootstrap.php
/docs
/dicas
AppController.php
bootstrap_cli.php
cake-acl-br-my.sql
cake-acl-br-pg.sql
custom.css
pdo_error.ctp
/src
/Controller
/Component
ControlComponent.php
/Template
/Bake
/Element
/Controller
add.ctp
delete.ctp
edit.ctp
index.ctp
login.ctp
logout.ctp
view.ctp
form.ctp
/Model
entity.ctp
/Template
add.ctp
edit.ctp
index.ctp
login.ctp
view.ctp
/Element
topmenu.ctp
/Layout
admin.ctp
default.ctp
/webroot
/css
bootstrap.min.css
bootstrap.min.css.map
/fonts
glyphicons-halflings-regular.ttf
/js
bootstrap.min.js
jquery.min.js
composer.json
LICENSE
README.md
Detalhando
alguns arquivos
config/bootstrap.php
Neste
arquivo são chamados o BootstrapUI e o código que lida com a
formatação das datas:
<?php
use Cake\Core\Plugin;
Plugin::load('BootstrapUI');
// Habilita o parseamento de datas localizadas
date_default_timezone_set('America/Fortaleza');
/**
* Locale Formats
*/
\Cake\I18n\Time::setToStringFormat([IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT]);
\Cake\I18n\Time::setToStringFormat('dd/MM/YYYY HH:mm');
\Cake\Database\Type::build('date')
->useLocaleParser()
->setLocaleFormat('dd/MM/yyyy');
\Cake\Database\Type::build('datetime')
->useLocaleParser()
->setLocaleFormat('dd/MM/yyyy HH:mm');
\Cake\Database\Type::build('timestamp')
->useLocaleParser()
->setLocaleFormat('dd/MM/yyyy HH:mm');
\Cake\Database\Type::build('decimal')
->useLocaleParser();
\Cake\Database\Type::build('float')
->useLocaleParser();
ini_set('intl.default_locale', 'pt_BR');
/**
* Default formats
*/
\Cake\I18n\Time::setToStringFormat('dd/MM/yyyy HH:mm:ss');
\Cake\I18n\Date::setToStringFormat('dd/MM/yyyy');
\Cake\I18n\FrozenTime::setToStringFormat('dd/MM/yyyy HH:mm:ss');
\Cake\I18n\FrozenDate::setToStringFormat('dd/MM/yyyy');
docs/AppController.php (resumido)
<?php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Event\Event;
class AppController extends Controller
{
// Controller default para usuários não admins em
protected $noAdmins = 'Customers';
public $helpers = [
'BootstrapUI.Form',
'BootstrapUI.Html',
'BootstrapUI.Flash',
'BootstrapUI.Paginator'
];
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('CakeAclBr.Control');
$this->loadComponent('Auth', [
'loginRedirect' => [
'controller' => 'Permissions',
'action' => 'index',
'base' => false
],
'loginAction' => [
'plugin' => false,
'controller' => 'Users',
'action' => 'login'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login'
],
'unauthorizedRedirect' => [
'controller' => 'Users',
'action' => 'login',
'prefix' => false
],
'authError' => 'Sem permissão para acessar esta área!',
'flash' => [
'element' => 'error'
]
]);
$this->set('titulo', 'Título do Aplicativo');
$user = $this->request->getSession()->read('Auth.User');
$loguser = $user['username'];
$this->set('loguser',$loguser);
$group=$user['group_id'];
$this->set('group',$group);
$controller = $this->request->getParam('controller');
$this->set('controller',$controller);
$action = $this->request->getParam('action');
// Via url: users/login?temp=default
// $layout=$this->request->query('temp');
// $this->set('template',$layout);
if($loguser == 'user' || $loguser == 'manager'){
$this->viewBuilder()->setLayout('CakeAclBr.default');
}else{
$this->viewBuilder()->setLayout('CakeAclBr.admin');
}
// Descomente para acesso total aos actions abaixo
// $this->Auth->allow(['index','add','edit']);
if(isset($group)){
// Popula a tabela permissions
// Após o primeiro login pode comentar as 3 linhas abaixo
$this->Control->populate(1);// Full permissions in all tables to users from Supers group
$this->Control->populate(2);// Full only in groups, users and permissions tables to users from Admins group
$this->Control->populate(3);// All in all tables that are not groups, users and permissions to all users from group Managers
// Enviar o controller atual para o element topmenu.ctp
$this->set('supers',$this->Control->tables($controller,$action,1));
$this->set('admins',$this->Control->tables($controller,$action,2));
$this->set('managers',$this->Control->tables($controller,$action,3));
$this->set('users',$this->Control->tables($controller,$action,4));
}
if($action != 'login' && $action != 'logout'){
if(isset($group) && $this->Control->access($controller,$action,$group)==false){
$this->Flash->error(__('Como usuário "'.$user['username'].'", você não tem permissão de acesso a '.$controller.'/'.$action));
return $this->redirect($this->referer());
}
}
}
public function isAuthorized($user)
{
$requestedUserId=$this->request->pass[0];
if ($user['group_id']==1){
return true;
} else if ($user['group_id']!=1 || $user['group_id']!=2) {
if (!($this->request->action == 'index')) {
if($userid==$user['id']) {
return true;
}
}
return false;
}
return parent::isAuthorized($user);
}
public function beforeRender(Event $event)
{
if (!array_key_exists('_serialize', $this->viewVars) && in_array($this->response->getType(), ['application/json', 'application/xml'])) {
$this->set('_serialize', true);
}
}
}
Um arquivo vital do plugin é o de configuração:
composer.json
{
"name": "ribafs/cake-acl-br",
"authors": [
{
"name": "Ribamar FS",
"email": "Este endereço de email está sendo protegido de spambots. Você precisa do JavaScript ativado para vê-lo.",
"homepage": "http://ribafs.org",
"role": "Developer"
}
],
"description": "Plugin para implementar controle de acesso com bons recursos: busca, bcrypt, template do bake em português, bootstrap",
"type": "cakephp-plugin",
"keywords": ["cakephp", "acl","bootstrap", "plugin", "busca", "admin", "painel", "bcrypt", "portugues", "controle de acesso"],
"license": "MIT",
"require": {
"php": ">=7.0",
"cakephp/cakephp": "~3.0",
"friendsofcake/bootstrap-ui": "1.4.1"
},
"autoload": {
"psr-4": {
"CakeAclBr\\": "src"
}
}
}
Veja
que este arquivo contém diversas informações importantes sobre o
plugin: nome do plugin, autor, descrição, dependências, licença,
autoload, etc.
Outro
arquivo importante é o README.md
Como
o README.md deste plugin é bem grande, não o incluirei aqui, mas
veja em:
https://github.com/ribafs/cake-acl-br
Após
concluir empacotei tudo no formato zip.
Envio
para o GitHib e publicação no Packagist
Como
em casa uso internet tipo ADSL, não posso enviar o código para o
GitHub usando Git e SSH na porta 22, que é a usada no GitHub. Então
procurei uma alternativa: enviar para a minha hospedagem, onde tenho
acesso SSH e de lá enviar via Git para o GitHub. Ou usar algum outro
servidor free que tenha suporte ao Git. Minhas alternativas são o
Google Cloud (https://cloud.google.com),
mas neste somente usando o Google Chrome. Ou usando o CodeAnyWhere
(https://codeanywhere.com/).
Depois
disso cadastrar o repositório no Packagist (https://packagist.org/).
Caso
precise de ajuda para criar o repositório no GitHub e publicar no
Packagist, para que possa instalar localmente via composer, então
confira o tutorial:
https://ribafs.org/portal/cakephp-3/tutoriais/projeto-no-github.html
Remover
plugin
composer
remove ribafs/cake-acl-br
Instalar
a versão em desenvolvimento
composer
require ribafs/cake-acl-br:dev-
master
Instalar
uma certa versão
composer
require ribafs/cake-acl-br:1.30
Carga
de Plugins
(exemplo)
$this->addPlugin(
'autoload'
=> true, 'bootstrap' => false, 'routes' => true
);
Recomendações
para o README de plugins
https://gist.github.com/josegonzalez/903066
Comments fornecido por CComment