Thursday, December 3, 2009

Re: Delete confirm

I thought it might be about time to jot down how I am implementing
this (based around AD7Six's code). I have got it working *pretty much*
as I want it, but still have a few questions. I never thought this
would be so tough! I readily acknowledge that I am stumbling in the
dark here at times.

First, I have amended my app_controller, adding:

$this->Security->blackHoleCallback = '_confirmAction';
$this->Security->requirePost('delete');

I am only really interested in trapping deletes at present, but can
soon resort to an array of actions later if needed. This stops a
delete request processing if it does not come from a POST. It's ideal
for me as the delete instruction comes from a link (and hence a GET).

Then I have my _confirmAction function:

function _confirmAction ($reason = null) {
if ($reason == 'post') {
$this->Security->_generateToken($this);
$this->redirect(array('action' => 'deleteConfirm', $this->params
['pass'][0]));
}
$code = 404;
if ($reason == 'login') {
$code = 401;
} else {
$this->Session->setFlash('Access Denied');
}
$this->redirect(null, $code, true);
}

This is called when a delete is attempted by anything other than a
POST. For my part, it safely redirects the user to the "deleteConfirm"
action. $this->params['pass'][0] refers back to $id passed in by the
delete link, and therefore contains the id of the record that is going
to be deleted.

Then I have my deleteConfirm action (it's in my app_controller so that
I don't have to propagate it throughout all of my controllers):

function deleteConfirm($id) {
$this->redirect(array('action' => 'view', $id, 'delete'));
}

You'll notice that I am redirecting the user to the view screen. I am
doing this because I want to present details of the record they are
going to delete rather than a reasonably anonymous screen. I am adding
an unnamed parameter - 'delete'. I'll explain that a bit more in a
second.

Here's where I stumble upon my first issue. I'd love to set a variable
called 'mode' to indicate what I am going to do in my 'view'. I have
tried $this-set('mode', 'delete'), but it seems to be cleared after
the redirect. Is this correct, or am I doing something wrong?

Back in my controller, I have amended my view function:

function view($id = null, $mode=null) {
$this->set('mode', empty($this->params['pass'][1]) ? 'view' : $this-
>params['pass'][1]);
...

Here I am inspecting the second passed parameter ($id first, then
$mode). If it is empty I am doing a straightforward view. Therefore,
the view renders without any reference to deleting. If, on the other
hand, $mode is not empty, I am doing something else. The only other
alternative at the moment is 'delete', so let's assume that's what's
happening. The view renders, but with some changes to reflect the fact
that I am deleting. This includes a health warning and a form to
perform the actual delete via a POST:

if ($mode == 'delete'):
echo $this->Html->tag('p', 'WARNING! You are about to delete the
following ' . $type . '. If you proceed, this delete cannot be
reversed.', array('class' => 'flashWarning'));
echo $this->Form->create(null, array('url' => array('action' =>
'delete', $id)));
echo $this->Form->input ($referer, array('type' => 'hidden',
'value' => $referer));
echo '<p><ul class="links">';
echo $this->Html->tag('li', $this->Form->submit ('Yes - delete',
array('div' => false)));
echo $this->Html->tag('li', $this->Html->link ('No - cancel', array
('controller' => $referer)));
echo '</ul></p>';
echo $this->Form->end();
endif;

I've also added a 'cancel' link that returns the user to where they
came from using $this->referer(). The action on the form is 'delete'.
As it comes from a POST the security component does not block it. So
the delete action in app_controller triggers (again, only have it in
app_controller as it is very standard):

function delete($id) {
if ($this->{$this->modelClass}->del($id)) {
$this->Session->setFlash(__('The record was deleted.', true), true,
array('class' => 'flashSuccess'));
$this->set('mode', 'view');
$this->redirect(array('action' => 'index'), null, true);
}
...

Here is where I run into my next couple of problems.

First, again I'd like to set $mode back to 'view', so that my next
function knows that it is not doing a delete. The redirect *appears*
to reset it, so it's an unrecognised variable.

Second, I can use $this->referer() to send the user back to somewhere
meaningful (rather than just the index), but (i) if it is a view
screen the deleted record isn't there so it's meaningless and (Ii) it
still has the second parameter ('delete'). I *can* handle both of
these in the view, but it isn't very elegant.

It's been a long post! To sum up, this is sort of working but it is a
bit clunky. I am sure there is a better way to pass the $mode variable
around and I am sure there is a more elegant way to set and deal with
$this->referer().

Any thoughts (please be gentle on me!)?

On Dec 3, 12:40 pm, AD7six <andydawso...@gmail.com> wrote:
> On 2 dic, 17:27, "j0n4s.h4rtm...@googlemail.com"
>
> <j0n4s.h4rtm...@googlemail.com> wrote:
> > A mostly working solution[1][2], that you can see here:http://github.com/ionas/sna/blob/master/www/app/app_controller.php#L1...
>
> > It is based on Teknoids great information at his blog[3][4], combined
> > with a helper that triggers a javascript::confirm(), doubleposts are
> > essentially not possible due to SecurityComponent. If you really
> > require an js-free confirmation, why not add a checkbox to that helper
> > that you have to check before clicking (check if it was clicked in the
> > helper and before that onSubmit via javascript)
>
> > [1]http://code.cakephp.org/tickets/view/377
> > [2]http://code.cakephp.org/tickets/view/354
> > [3]http://teknoid.wordpress.com/2008/11/05/make-your-cakephp-forms-a-lot...
> > [4]http://teknoid.wordpress.com/2008/11/06/clearing-up-some-confusion-re...
>
> That's ... a lot of code. You also have to remember/know in the view
> if you should or should not use your helper.
>
> FWIW I prefer for things to be a lot more transparent that that.
> Here's an example:http://dl.dropbox.com/u/1027790/csrf-protect-confirm.png
>
> That's using ajax - but it doesn't have to be (it's not a requirment
> or inherant to the technique/solution).
>
> The 'magic' is here:http://code.assembla.com/mi/subversion/nodes/branches/mi_plugin/views...
>
> There's no 'magic' here:http://code.assembla.com/mi/subversion/nodes/branches/blog/views/entr...
>
> Anyway, good for the topic to be discussed.
>
> AD

Check out the new CakePHP Questions site http://cakeqs.org and help others with their CakePHP related questions.

You received this message because you are subscribed to the Google Groups "CakePHP" group.
To post to this group, send email to cake-php@googlegroups.com
To unsubscribe from this group, send email to
cake-php+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/cake-php?hl=en

No comments: