Multiple prefixed routes and DRY principle - php

I use CakePHP 2.2.7
In my app I have a public area and admin area.
I use prefixed routes so for admin actions I use
admin_index() etc.
Now I need to add additional admin area for managers. This manager area will be different in some cases against the admin area.
Different layout, not all actions allowed.
My question is:
Should I simply duplicate actions which already implemented for admin area (and add another prefix, for example manager_index() ) or there is a more simple and DRY solution?

You can do this for example
public function manager_edit($fooId = null) {
$this->admin_edit($fooId);
}
But if you did a good job most of your code should be already in the model and your code look like this (just a basic example);
public function manager_edit($fooId = null) {
if ($this->Foo->edit($fooId, $this->request->params, $this->Auth->user('id')) { /*....*/ }
}

Related

How to Dynamically add gates in Laravel?

How to Dynamically add gates in Laravel?
I built my CMS on laravel, and it support plugins. Plugin can add menu item in dashboard, some page , routes etc. I need use gate for protect edit / delete / add / change page for example, that an authorized user did not create.
So I need that each plugin can Dynamically add this permissions (view /add/edit/update/delete/)
How I can do this?
And btw plugin locate in APP/Plugins folder.
And Admin can attach this permission to some roles http://prntscr.com/kidnxz
https://pastecode.xyz/view/bcb9d00f here example table that I have.
and problem that I need also add in permissions table permission
I use laravel 5.6
Here example of simple plugin pastecode.xyz/view/75d2fa28 Calls boot when plugin loaded And onActivate when user activate this plugin
I'm looking for something like dynamic Policies
or like https://octobercms.com/docs/plugin/registration
'permissions' => ['acme.blog.*'],
You can use Gate::policy(PolicyTarget::class, PolicyImplementation::class) to register a new gate policy inside your plugin.
Assume you have a policy target being e.g. a user (bad example maybe).
Create a policy with e.g. php artisan make:policy UserInteractsWithBlogPolicy
class UserInteractsWithBlogPolicy {
// Boilerplate and custom code
}
Then you can register this when the plugin boots:
public function boot() {
// TODO: Implement boot() method.
$this->enableRoutes();
$this->enableViews();
\Gate::policy(User::class, UserInteractsWithBlogPolicy::class);
}
Then you can use the normal policy rules like e.g.
$user->can("view", Blog::find(1));
Or basically do whatever can be done according to https://laravel.com/docs/5.6/authorization#authorizing-actions-using-policies
As far as I understood from your question your CMS may has several gates that you do not want to define separate gates for each of them. You may try before method to define dynamic gates.
public function boot(){
Gate::before(function (User $user, $ability) {
Gate::define($ability, function (User $user) use ($ability) {
return $user->hasPermission($ability);
});
});
}
In the above example hasPermission is a method in User model that check if user has the right permission/ability.
Now you can use it like this in controller:
Gate::authorize('CUSTOME_ABILITY');
Or as middleware:
Route::get('/', function (Request $request) {
// your code
})->can('CUSTOME_ABILITY');

PHALCON PHP - Different views based on user profile

I created Phalcon PHP app,
I have 3 different user profiles: (ID: 1) Administrators, (ID: 2) Accountants and (ID: 3) Warehouses.
I want my app able to render views based on those profiles, for example
controllerName/index.1.volt //for Administrators
controllerName/index.2.volt //for Accountants
controllerName/index.3.volt //for Warehouses
but when those files weren't found, my app will fallback to:
controllerName/index.volt
How do I accomplish that?
One approach, although messy, would be to use controlerName/index.volt as the "landing page" then from there check an if-statement to decide what the user's role is. Then from the if-statement use a partial like {{ partial("index.1.volt") }} but you'd need to hard-code this for every controller... yuck...
A good solution which I'd recommend, though, would be to make use of the View's exists method to check if the view exists from within your controller. The idea is you pass this method the string path to the view file you're looking for but omitting the extension. The reason you omit the extension is because Phalcon allows you to use multiple rendering engines so an application using a mixture of .volt and .phtml would work.
Assuming you were using user roles something like this:
define('GUEST_ROLE',0);
define('ADMIN_ROLE',1);
define('ACCOUNTANT_ROLE',2);
define('WAREHOUSE_ROLE',3);
(with the guest role with a value of 0) I would suggest having all your controllers extend a ControllerBase then define the following two methods in your ControllerBase:
public function getUserLevel()
{
if($this->session->has('userLevel'))
{
$userLevel=$this->session->get('userLevel');
return (int)$userLevel;
}else{
return 0;//default to guest
}
}
protected function initialize()
{
$controllerName=$this->dispatcher->getControllerName();
$actionName=$this->dispatcher->getActionName();
$userLevel=$this->getUserLevel();
if($this->view->exists("$controllerName/$actionName.$userLevel"))
{
$this->view->pick("$controllerName/$actionName.$userLevel");
}
//No reason to add an else, Phalcon defaults to "$controllerName/$actionName"
}
Just make sure, that if you ever need to define a custom "initialize" method for a specific controller which extends the ControllerBase, e.g. to add a title prefix to all pages related to the controller, that you call parent::initialize(); otherwise it won't get called. But that's only if you're going to be overriding the method.
Chances are you're already using a ControllerBase and doing similar logic already, if so, you'd need to edit your already existing "initialize" method and merge my code with yours.
Happy coding.

How to insert database info into a layout with Zend Framework

I have started to learn Zend Framework. I have done the quickstart tutorial and the akrabat tutorial.
Now I am trying to work on some of my own project with Zend Framework and it is a frustrating experience to say the least.
I made a layout that creates my standard header that shows up on every page. Now I want to display the user's name in the header which is retrieved from a mysql database. I don't know how to go about it. Do I interact with a controller and model from the layout??
Also, are there any other good guides for this kind of information? The zend documentation seems to be very detailed for each component but not very good at explaining how things work together.
Within your layout you will need this
<?= $this->layout()->content ?>
This will output anything from your view script.
In your project structure you need a controller and a view script for that controller. You should know how to access this if you have done the tutorial.
Your controller should get the username from the DB and assign it to the view like so
$this->view->username = "John";
Then in your view
echo $this->username;
EDIT
In your Bootstrap class register a new plugin
$plugin = new Default_Controller_Plugin_Username();
Zend_Controller_Front::getInstance()->registerPlugin($plugin);
This plugin might look like this
class Default_Controller_Plugin_Username
extends Zend_Controller_Plugin_Abstract
implements Lib_Observer_Observable
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
//Do things in here to get username
//Then you can set it in the registry
Zend_Registry::set("username", $username);
}
}
If you have the $user from some kind of auth/login sequence, then you might be using Zend_Auth. The default container for the identity data is session-based, so depending upon your session-handling settings, you might not need to hit the db each time to get that user info.
The bigger question to me is whether you need a front controller plugin to place this $user info from Zend_Auth into the view (to be rendered in your layout) or whether the layout can pull from Zend_Auth directly.
I've seen (and used) both.
Your problem is the problem of the block composition on the page. As you may know Zend Framework is only a Framework, so there's really more than one way to deal with this problem.
To fill the layout part you could for example use :
some variable containing the block and given to the view for each controller (with a controller plugin? a main Controller that all controllers inherits?
the Action Helper in the view to chain an internal call to another MVC call in this part of the layout
the Action stack
certainly others things
I think the nicest one is the Action stack. The way it work is:
You make a main query on the MVC-thing (where you get yout fooAction called in a controller)
You call the Action stack in this Action to tell ZF that some others internal calls on some other Action should be done. Here you can list all blocks that should be filled in the Layout
Others bloacks are called by the internal MVC loop. Some other barAction userAction entry points are called, in these actions you tell the Layout which key of the layout is filled by the resulting view rendering with setResponseSegment.
Here's how it looks in code:
For the block action (here a /modulefoo/titi/pilili internal request), that should fill the 'foobar' block of the Layout :
public function pilipiliAction() {
(...) // do things
$this->_helper->viewRenderer->setResponseSegment('foobar');
}
On the layout you echo this block with:
<?= $this->foobar ?>
Now this action must be called by your main action (with the Action Stack helper). But the nicest way will be to use a new custom helper that will stack all the blocks actions.
Here 's a fooAction on a controller, your classical entry point. At the end of the action an helper is called that should build the blocks of the layouts
public function fooAction() {
(...) // do things
// Build Main layout
$this->_helper->LayoutBuilder();
}
Here's an example of what this helper would do (should work, it's a simplified version of an existing one which makes a lot of others things, like managing cache for blocks):
class My_Action_Helper_LayoutBuilder extends Zend_Controller_Action_Helper_Abstract
{
/**
* #var $actionStack
* local ActionStack builtin helper used to Stack several MVC actions,
* for action for each Layout block
*/
protected static $actionStack;
/**
* #var _layout
* reference to the general layout collecting view
*/
protected $_layout;
public function __construct() {
self::$actionStack = Zend_Controller_Action_HelperBroker::getStaticHelper('actionStack');
$this->_layout = Zend_Layout::getMvcInstance();
}
public function direct($blocklist = null) {
$layoutblocks = array(
array('module' => 'default',
'controller' => 'index',
'action' => 'nav')
,
array('module' => 'modulefoo',
'controller' => 'titi',
'action' => 'pilipili') // action given upper
);
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$request = Zend_Registry::get('request');
$module = $request->getModuleName();
$layoutblocks[] = array('module' => $module,
'controller' => 'index',
'action' => 'modulenav');
}
if (isset($blocklist))
{ //user had his own blocklist to add
$layoutblocks = array_merge($layoutblocks,$blocklist);
}
$this->buildCompositeLayout($layoutblocks);
}
public function buildCompositeLayout($blocklist = null) {
foreach($blocklist as $MVCBlock) {
$block = $MVCBlock['action'];
if (!isset($MVCBlock['module'])) {
$module = $this->getRequest()->getModuleName();
$front = $this->getFrontController();
$module = $front->getDispatcher()->getDefaultModule();
} else {
$module = $MVCBlock['module'];
}
if (!isset($MVCBlock['controller'])) {
if (!isset($front)) {
$front = $this->getFrontController();
}
$controller = $front->getDispatcher()->getDefaultController();
} else {
$controller = $MVCBlock['controller'];
}
// Here we stack actions
self::$actionStack->actionToStack($block,
$controller,
$module,
);
}
}
But as I said before, there's more than one way to do it. I like this way as I can interactively add new blocks for some requests, add some caching in the block rendering, and I can as well 'forget' to call the LayoutBuilder helper for some actions, like the ajax ones. The secret is that the MVC loop of Zend Framework is really a loop; it can run several actions for one request.
Zend_Auth rocks.. you need to use it.
There's a good tutorial on Zend_Auth here:
http://akrabat.com/zend-auth-tutorial/
This includes the process of creating a View Helper to do the repetitive task of displaying the Username, role and a logout link.
Take the time to come to grips with View Helpers.. they really explode your productivity and code reuse when coding views.
Duncan.
#Mike, this answer from #jakenoble is exactly correct for your scenario.
If you want to perform some global actions in your controllers then i suggest to create your own base controller class inherited from Zend_Controller say MyBaseController.
Then you can inherit all your controllers from MyBaseController, this way all the actions performed in init() method of MyBaseController will global to your complete project.

Having actions/pages without requiring a login

I am building an application using cakePHP. Do we have a method where we can allow public users access to certain pages without logging in. There would be a few pages such as about us regarding the whole organisation or a contact us page. Is there a method to avoid login access, something similar to how we have ways to add components or set layouts.
As Martin Bean says, you can use ACL. For a sophisticated site, that would be my choice. You do not have to be logged in to access the public pages. http://multiheadweighers.co.uk is an example of a site that uses ACL. There is a fully featured CMS behind the public pages.
For a simple site I would allow access to, for instance, the view action using
function beforeFilter() {
parent::beforeFilter;
$this->Auth->allow('view');
}
see: http://book.cakephp.org/view/1257/allow
It really isn't a big deal - try it and you'll see how easy it is.
EDIT:
From the book # http://book.cakephp.org/view/1550/Setting-up-permissions
Now we want to take out the references
to Auth->allowedActions in your users
and groups controllers. Then add the
following to your posts and widgets
controllers:
function beforeFilter()
{
parent::beforeFilter();
$this->Auth->allowedActions = array('index', 'view');
}
This removes the 'off switches' we put in earlier on the users and groups controllers, and gives public access on the index and view actions in posts and widgets controllers. In AppController::beforeFilter() add the following:
$this->Auth->allowedActions = array('display');
This makes the 'display' action public. This will keep our PagesController::display() public. This is important as often the default routing has this action as the home page for you application.
EDIT 2:
$user = ($this->Auth->user())?$this->Auth->user():'Anonymous';
if(!$this->Acl->check($user,"{$url}"))
$this->redirect($this->referer()); // or whatever action you want to take.
The solution would be to use the allow method in the Auth component to let the user visit those pages.
Thank you!

Making static pages in Symfony 1

Im new to symfony and have some simple questions. I am trying to understand the module system, but I dont understand how I create the actual homepage or other pages that are not based off of a model from the db. For example, the simple about page that has static info or the homepage that is a combination of a bunch of information from different models.
Can anyone help?
First of all, modules do not have to be restricted to a model from the database. You can have a Foo module which relies on no database content, and a Bar module that is primarily based on 3 different models. The module separation is a way to logically break up your site into manageable sections. Eg an e-commerce site might have a Products module, a Categories module and a Cart module and so on.
Your last sentence can then be split into 2 parts:
1) Static information can be on any page - if it's for things like "About us" and "FAQ" etc, I personally tend to use a "default" or "home" module, and create the various actions in there vis:
./symfony generate:module appname home
and
class homeActions extends sfActions
{
public function executeAbout(sfWebRequest $request)
{
// ...
}
public function executeFaq(sfWebRequest $request)
{
// ...
}
}
with the corresponding template files (aboutSuccess.php, faqSuccess.php).
2) A page can be comprised of data from many different models - just use your preferred ORM's method of retrieving data and set it to the view ($this->data = MyModel->findByColumn(...) etc). If you mean data from different modules, then you'd probably be better off looking at partials or components for elements of a page that can be used across different modules (navigation etc). See the Symfony docs for more details on these.
I'm used to handle static pages in this way.
First I create a new entry in apps/frontend/config/routing.yml:
page:
url: pages/:page
param: { module: page, action: index }
Then I write a "page" module (apps/frontend/modules/page/actions/actions.class.php):
<?php
class pageActions extends sfActions
{
public function executeIndex()
{
$this->page = $this->getRequestParameter("page");
$this->forward404Unless($this->_partialExists($this->page));
}
protected function _partialExists($name)
{
$directory = $this->getContext()->getModuleDirectory();
return (is_readable($directory.DIRECTORY_SEPARATOR."templates".
DIRECTORY_SEPARATOR."_".$name.".php"));
}
}
Last step, put in modules/page/templates/indexSuccess.php this code:
<?php include_partial($page); ?>
So all you have to do from now is to create a partial for each static page ie.
apps/frontend/modules/page/templates/_home.php which you can reach at
http://yousite/pages/home (without the need to add a new routing entry for every page)
You can create a module, e.g. called static and create actions for every static page or only one action that delivers the page depending on a request variable. The only thing this action does is loading a template.
IMHO it would be good if symfony comes with a default module for this.
For example actions of (my custom) module static:
class staticActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
if(!$request->hasParameter('site')) {
return sfView::ERROR;
}
$this->site = $request->getParameter('site');
}
}
With this template:
//indexSuccess.php
<?php include_partial($site) ?>
The actual statics sites are all partials.
In my routing.yml looks like this:
# static stuff
about:
url: /about
param: {module: static, action: index, site: about}
This way you only have to create a new partial and a new routing entry when you add a static site and you don't have to touch the PHP code.
Another way to serve static pages without having to write any controller code is to set up the route something like the following:
myStaticPage:
pattern: /pageName
defaults:
_controller: FrameworkBundle:Template:template
template: MyBundle:Home:pageName.html.twig
Then just create your twig template and it should work fine.
Apart from the above, consider having a CMS for static pages, so you won't need technical savy people to mantain them or change them. This depends on the project, of course.
For really static and independent pages you can simply create any file in [pathToYourProjectRoot]/web directory.
It may by i.e. [pathToYourProjectRoot]/web/assets/static_html/about.html.
Then link to the page directly by http://your.site.com/assets/static_html/about.html.

Categories