Modifying view based on ACL in CakePHP - model-view-controller

I want to be able to show or hide certain elements in a view based on ACL. For instance, if a user is looking at my Users/index view, I don't want to show a 'Delete User' element if he doesn't have permission to delete users. If he does have permission to edit users, I do want to show a 'Edit User' link.
I can hack this together, but being very new to Cake I'm hoping that there is an elegant solution. The best I've done involves keeping logic in two places, so it's hell to maintain.
Thanks!

I know this is an old question now but for anyone looking for a way like I was...
In AppController::beforeFilter you can assign the ACL component to a view variable and then use it in your view:
$this->set('user', $this->Auth->user());
$this->set('acl', $this->Acl);
And then in you view just juse it like thie:
if($acl->check(array('User' => $user), 'controllers/groupd/admin_delete')) {
This is't necessarily the most correct way to do it but it does work nicely

There is no generic "elegant solution" :) I've always wanted to make such thing as well. Anyway how you could do it:
Overwrite the Html Helper in your app directory - make a copy from /cake/libs/views/helpers/html.php to /app/views/helpers/html.php and made some changes in the Html::link function.
For example you can check if the url contain action edit or delete.
The other part is to pass the proper parameters from the controller. In AppController::beforeFilter you can read the rights of the user (it's better to be cached) and to pass it in a special Auth variable to the View.
So when you have the rights in your View it's easy to modify the link. :)
As I said I haven't did it in real example, but this is the way I would do it.
There is 1 bad point in that - if the original Html helper is changed, your one will remain the same. But I believe that Html helper is mature enough so for me is not a big issue.

I do it like this in app_controller.php, although you could just as well do it in specific controllers. The view variables $usersIndexAllowed and $configureAllowed are then used in conditional statements in the view.
function beforeRender()
{
if($this->layout=='admin')
{
$usersIndexAllowed = $this->Acl->check($user,"users/index");
$configureAllowed = $this->Acl->check($user,"siteAdmins/configure");
}
$this->set(compact('usersIndexAllowed','configureAllowed'));
}

In case you don't want to mess around with overriding core helpers and you want a more automatic way of checking (without hard-coding user group names and users or setting separate link-specific variables) here's my suggestion:
Store all user permissions as session vars when the user logs in (clear on logout) and create a permissions helper to check if logged on user has permissions for a specific action.
code and example here
hope that helps

There's multiple approaches to this scenario. As Nik stated, using a helper to do the checks for you is a quick way to "outsource" the logic and centralize it for ease of use.
Actually, have a look at the AclLinkHelper - it does exactly what you're looking for, however restricted to links only.

Related

Bitrix CMS, how to get cached data based on GET parameter in template of standart component?

I'm working with a component bitrix:catalog (which is standard one) and faced an issue. I want to add some extra GET parameters to switch view mode. I think there is no need to rewrite whole component to make such switcher, so I added extra keys in result_modifier in a way similar to:
$this->__component->arResultCacheKeys = array_merge($this->__component->arResultCacheKeys, array('key1', "key2"));
Earlier in the same result_modifier I perform adding those extra keys in $arResult['key1'] etc. They seem to be correctly saved, but only for current inquiry such as ?view=list or view=card, that means only one variable value is saved and it does not react on changing of GET parameter. Is there simple and correct way to make that component to cache and to output data based on GET variable? The only idea which came to my mind is to rewrite component by adding extra parameter and checking of GET, but I think there must more simple and correct solution to make in via template. Human Readable Links are turned on. And I want to have auto-cash being turned on as well. If I turn it off it starts working as planned.
One of possible solutions is to rewrite it cache by SetTemplateCachedData but it still seems to me rough and incorrect way for such simple task.
Bitrix masters please help me to find correct solution, google can't help at the moment.
If you use standard bitrix:catalog component, you may be use standart bitrix:catalog.section. In that component.php used standart component cache.
That means you can describe additional parametr in you custom .parameters.php, and set it in bitrix:catalog.section params.
Standart component cache set cacheId based on arParams.
So you include component should look like this:
$APPLICATION->IncludeComponent(
"bitrix:catalog.section",
"",
array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"ELEMENT_SORT_FIELD" => $arParams["ELEMENT_SORT_FIELD"],
"ELEMENT_SORT_ORDER" => $arParams["ELEMENT_SORT_ORDER"],
....
....
"NEW_ADDITIONAL_GET_PARAMS"=> $_GET['view']
),
$component
);
Of course better way somethink like
"NEW_ADDITIONAL_GET_PARAMS"=> (in_array($_GET['view'],array('list','card'))?$_GET['view']:'list')
But may be you need just set right catalog params: SEF_MODE SEF_FOLDER SEF_URL_TEMPLATES

Magento Admin Panel Customization

In Manage Customers tab, by default last 20 registered users are listed with their emails. I would like to hide the list or change it only 1. This is being done to make sure that call center agents are not easily copying customer's email information.
I am very new to this so, if its not too much trouble, provide full path to the file.
Steve
You might want to go a slightly different direction, by developing a set of permissions for your Agents, so that perhaps they can't access the email in the first place, if you don't want them to see that information.
If you want to modify the page size, you can rewrite this class
Mage_Adminhtml_Block_Customer_Grid
And you'll want to override this method:
protected function _preparePage()
{
parent::_preparePage();
$this->getCollection()->setPageSize(1);
}
You may also want to get rid of the pagination dropdown in
app/design/adminhtml/default/default/template/widget/grid.phtml
because it will be confusing for them to see it but when they use it for it to not function.

What is The Rails Way for requesting an alternate view of all records?

I have a rails app that has a list of Products, and therefore I have an index action on my ProductsController that allows me to see a list of them all.
I want to have another view of the products that presents them with a lot more information and in a different format -- what's The Rails Way for doing that?
I figure my main options are:
pass a parameter (products/index.html?other_view=true) and then have an if else block in ProductsController#index that renders a different view as required. That feels a bit messy.
pass a parameter (products/index.html?other_view=true) and then have an if else block in my view (index.html.haml) that renders different html as required. (I already know this is not the right choice.)
Implement a new action on my controller (e.g.: ProductsController#detailed_index) that has it's own view (detailed_index.html.haml). Is that no longer RESTful?
Is one of those preferable, or is there another option I haven't considered?
Thanks!
Another way of doing it would be via a custom format. This is commonly done to provide mobile specific versions of pages, but I don't see why the same idea couldn't be applied here.
Register :detailed as an alias of text/html and then have index.detailed.haml (or .erb) with the extra information. If you need to load extra data for the detailed view you can do so within the respond_to block.
Then visitors to /somecollection/index.detailed should see the detailed view. You can link to it with some_collection_path(:format=>'detailed')
I'm not sure whether this is 'bettrr' than the alternatives but there is a certain logic I think to saying that a detailed view is just an alternative representation of the data, which is what formats are for.
After doing some reading, I think that adding a new RESTful action (option #3 in my question) is the way to go. Details are here: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
I've updated my routes.rb like this:
resources :products do
get 'detailed', :on => :collection
end
And added a corresponding action to my ProductsController:
def detailed
# full_details is a scope that eager-loads all the associations
respond_with Product.full_details
end
And then of course added a detailed.html.haml view that shows the products in a the detailed way I wanted. I can link to this with detailed_products_path which generates the URL /products/detailed.
After implementing this I'm sure this was the right way to go. As the RoR guides say, if I was doing a lot of custom actions it probably means I should have another controller, but just one extra action like this is easy to implement, is DRY and works well. It feels like The Rails Way. :-)

codeigniter extra url segments

I am making a site for a client and decided i would use code igniter.
The site essentially has two backends, one for designers, and one for a sales team. So after logging in, the user will be redirected to either
mysite.com/sales/
mysite.com/design/
The sales team for example can view orders, containers, products, therefore i need a controller for each of these.
mysite.com/sales/orders/
The sales need to be able to view, edit, delete certain orders...
mysite.com/sales/orders/vieworder/235433
Basically my problem is i dont have enough url segments to play with.
My thoughts on solving my problem
removing the orders, containers, products classes and making ALL of their methods as methods of the sales class, although this would mean a large number of methods and loading all models so it seemed kind of pointless.
removing the sales/designer classes and controlling what each kind of user has access to based on a user type stored in session data.
a way of having an extra url segment?
I appreciate any advice, I just dont want to get 3 weeks into the project and realise i started wrong from the beginning!
Use folders.
If you make a subfolder in /application/ called sales you can put different controllers in there:
/application/
/sales/
orders.php /* Controller */
/design/
Then in orders.php you will put your vieworders($id) method and so on, and you will be able to acces it with domain.com/sales/orders/vieworders/id.
You can also make subfolders in the /models/ and /views/ to organize your files.
Now, Access Control is something apart and it depends more in the auth system you are using.
Give the user/designer a privilege, a column in the user table for example, check the permission of the user at the beginning of the function, then prevent or execute it.
This would be the exact way i would do it.
Seems like you should have a look into the routing class. Might be a dirty solution but rerouting the sales/(:any) to something like sales_$1 would mean you'd make controllers with names like sales_orders.
Same for the design part.
(FYI: $routing['sales/(:any)'] = 'sales_$1'; should do the trick; see application/config/routing.php).

Cheating traversal

I'm developing a plone4 site on which every user have a sortable inventory of items. The ATFolder's folder_content view is ideal for this. The only problem is that instead of an URL like this:
/site/user/inventory
or this
/site/inventory/user
the url should be:
/site/inventory
I've thought in several solution, but each one have its own doubts.
Make the inventory content dynamic, depending on the authenticated user. I don't even know if this is possible on plone.
Somehow to cheat the transversal mechanism, so /site/inventory render /site/inventory/user.
Change the context before rendering the view. Again, don't know if possible.
Make inventory a subclass of ATCTContent, store the inventory data as annotation on the user and develop the ordering code all by myself. This is the option I'm trying to avoid.
What would you do?
Thanks.
Well, it'll be easy to define a inventory view that then uses the Authenticated User to render it's contents, which could be a simple delegation to an ordered folder that is stored at /site/users/user/folder.
The one thing that you have to remember is that user authentication happens after traversal. This means that when a view is instantiated (it's __init__ method is called) there is no user determined yet because that happens during traversal. Look up your user in the view __call__ or from it's template instead.
Having folder contents show contents that are not the contents of the folder is crraaaaAAAAzytalk. :) Don't do it. Either have a folder per user ( /inventory/user ) or make a custom view called inventory.html. You can make /inventory sho /inventory user but that is one step towards trying to make Plone to non-ploneish things, and that way lies a world of pain.
I don't know why you couldn't just call it /inventory/user? Seems easy enough. Then stick an action in the user viewlet, by the dashboard link, and your done! :-)
Plone is a content management system. Use it for that, as it's supposed to be used, and you'll be happy. Trying to force it to do things it doesn't want is like trying to build a sportscar out of a art deco sculpture. It might end up looking awesome, but it won't run very well. :-)

Resources