Don't you speak portuguese? Translate this site with Google Translator

Pensamento do Dia

Qualquer um pode zangar - se, pois isso é muito simples. Mas zangar - se com a pessoa adequada, no grau exacto, no momento oportuno, com o propósito justo e de modo correcto, isso, não é tão fácil como isso. (Aristóteles - Ética a Nicómaco)

11.3 - Criação do plugin cake-acl-br

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

Novo Testamento

Aquele, porém, que atenta bem para a lei perfeita da liberdade, e nisso persevera, não sendo ouvinte esquecidiço, mas fazedor da obra, este tal será bem-aventurado no seu feito.
(Tg, 1:25)

Rotas no Mapa do Google

© 2015 Ribamar FS. All Rights Reserved. Designed By JoomShaper