Owner Credential & Propel Admin Generator
It would be great to be able to specify in generator.yml a per object owner credential, so the generator only showed that actions to the owner of each object. Lets see how we can achieve this behavior easily.
Setting up the project.
Install and Configure Symfony
Follow the instructions of the great jobeet tutorial to install symfony and create the frontend application. We will use the propel ORM.
Instaling sfGuardPlugin
Following the symfony philosophy, we do not need to reinvent the wheel.
$ symfony plugin:install sfGuardPluginPreparing schema and some sample data
# config/schema.yml
propel:
post:
id:
title: { type: varchar(255), required: true }
content: { type: longvarchar, required: false }
owner_id:
type: integer
foreignTable: sf_guard_user
foreignReference: id
required: true# data/fixtures/fixtures.yml
sfGuardUser:
andrew:
username: andrew
password: andrew
valentine:
username: valentine
password: valentine
Post:
-
title: Andrew's Post
content: This is the content of the first Andrew's post.
owner_id: andrew
-
title: Valentine's Post
content: This is the content of the first Valentine's post.
owner_id: valentineBuild models, load data and create the post module
$ symfony generate:app frontend $ symfony propel:build --all --and-load $ symfony propel:generate-admin frontend Post $ symfony plugin:publish-assets $ symfony clear cache
The funny work
At this point, if you go to the url ocag.localhost/post, we can see the list of posts, with two actions attached to each post: edit and delete.

What we want is that the admin-generator only shows the actions edit and/or delete to the owner of each post.
Enabling login module and securing post module
Add the following to settings.yml
# apps/frontend/settings.yml
# ...
all:
.settings:
# ...
# Modules
enabled_modules: [default, sfGuardAuth]
.actions:
# ...
login_module: sfGuardAuth
login_action: signin
secure_module: sfGuardAuth
secure_action: secureSecure the post module
# apps/frontend/modules/post/config/security.yml default: is_secure: true
Edit the generator.yml
First, we set the owner credential to an object action.
What we woult like the admin generator to do with this, is to hide that action to everybody but the creator of the object being procesed.
Saddly this is not way the default admin generator works, but we will soon see how to build our own customized admin theme with this behavior.
Don’t be afraid! It’s easyer than it appears.
# apps/modules/post/config/generator.yml
generator:
class: sfPropelGenerator
param:
model_class: Post
theme: admin
non_verbose_templates: true
with_show: false
singular: Post
plural: Posts
route_prefix: post
with_propel_route: 1
actions_base_class: sfActions
config:
actions: ~
fields: ~
list:
#
# setting owner credential
#
object_actions:
_edit:
credentials: owner
_delete:
credentials: owner
filter: ~
form: ~
edit: ~
new: ~Creating myadmin theme
Create the folder data/generator/sfPropelModule/myadmin and copy the admin theme files in order to customize them:
$ mkdir -p data/generator/sfPropelModule/myadmin $ cp -r lib/vendor/symfony/lib/plugins/sfPropelPlugin/data/generator/sfPropelModule/admin/* \ data/generator/sfPropelModule/myadmin
Configure the generator.yml to use myadmin theme
# apps/modules/post/config/generator.yml
generator:
#...
param:
#...
theme: myadminThe partial-generator responsible of the object_actions is data/generator/sfPropelModule/myadmin/template/templates/_list_td_actions.php. We are going to add a line that calls a user function that dinamically adds or revoques the owner credential depending on the object being processed.
<?php echo '<?php $sf_user->addOwnerCredentials($' . $this->getSingularName() . ') ?>' ?> <td> <ul class="sf_admin_td_actions"> <?php foreach ($this->configuration->getValue('list.object_actions') as $name => $params): ?> <?php if ('_delete' == $name): ?> <?php echo $this->addCredentialCondition('[?php echo $helper->linkToDelete($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?> <?php elseif ('_edit' == $name): ?> <?php echo $this->addCredentialCondition('[?php echo $helper->linkToEdit($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?> <?php else: ?> <li class="sf_admin_action_<?php echo $params['class_suffix'] ?>"> <?php echo $this->addCredentialCondition($this->getLinkToAction($name, $params, true), $params) ?> </li> <?php endif; ?> <?php endforeach; ?> </ul> </td>
Now edit myUser.class.php to code the addOwnerCredentials method
// apps/frontend/lib/myUser.class.php class myUser extends sfGuardSecurityUser { protected static $OWNER_CREDENTIAL = 'owner'; /** * If the user is the owner of the $object adds owner credentials, * otherwise remvoques it. */ public function addOwnerCredentials($object) { if (!$this->isAuthenticated()) { return false; } if (!$object) { return false; } if (get_class($object) == 'sfOutputEscaperObjectDecorator') { $object = $object->getRawValue(); } if (method_exists($object, 'isOwner') && $object->isOwner($this->getGuardUser())) { $this->addCredential(self::$OWNER_CREDENTIAL); } else { $this->removeCredential(self::$OWNER_CREDENTIAL); } } }
Tunning Post model
The only thing we have to do now is to add the isOnwer method to each model that we want to be checked against owner credential, Post model in our case. So lets edit lib/model/Post.php
// lib/model/Post.php class Post extends BasePost { public function isOwner($sf_guard_user) { return $this->owner_id == $sf_guard_user->getId(); } }
Try it
And thats all. Clear caches, login as Andrew or Alice and see how the admin generator only shows the action links to the owner of each post. See the post list logged as Andrew:

Very immportant final comment
Remember that the only thing that we have done with this is to automatize the view layer generated by the admin generator. You still must securize the edit and delete actions of each module in order to avoid undeserable people to be able to execute them.
