Stumped on Drupal 7 - adding user_profile_form #validation callback via hook_form_alter - drupal-7

I've searched, read, and re-read. I've even stripped a test module down to the most basic elements.
I think this code should work, but the validation callback is never called. I add the callback to the form, but it is gone at the next steps.
Ultimately, my goals is to "validate" submitted usernames, in order to meet a specific business objective. I'm getting stuck just adding the form validation callback, but I'm confident that once that hurdle is overcome, the rest of the functionality is straight forward.
Can someone smarter than me please point me in the right direction to properly add a validation callback?
<?php
/*
Implements hook_form_alter();
*/
function mymod_form_alter($form, $form_state, $form_id) {
// catch the user profile form.
if('user_profile_form' == $form_id) {
// set debug message
drupal_set_message(t('adding validation callback in hook_form_alter()...'), 'warning');
// add a validation callback
$form['#validate'][] = 'mymod_user_profile_form_validate';
}
}
/*
Implements hook_form_<form_id>_alter();
If mymod_form_alter() was successful, I wouldn't need this hook, it's just here for verification.
*/
function mymod_form_user_profile_form_alter($form, $form_state, $form_id) {
// check to see if our validation callback is present
if(!in_array('mymod_user_profile_form_validate', $form['#validate'])) {
// our validatiation callback is not present
drupal_set_message(t('The validation callback is missing from #validate in hook_form_[form_id]_alter()'), 'error');
// since it's not there, try re-adding it?
$form['#validate'][] = 'mymod_user_profile_form_validate';
} else {
// We should see this message, but don't.
drupal_set_message(t('The validation callback exists!'), 'status');
}
}
/*
?? Implements hook_form_validate(); (or not...) ??
*/
function mymod_user_profile_form_validate($form, $form_state) {
// why is mymod_user_profile_form_validate() never called??
drupal_set_message(t('Validation callback called! Whoopee! Success!'), 'status'); // This never happens!
// if this was working, we would do a bit of logic here on the username.
//for this test, let's assume we want to return an error on the username field.
form_set_error('name', t('Blah, blah, blah, something about your username...'));
}

The variables $form and $form_state should be passed by reference so that your hook function can actually modify them (otherwise the function just get a copy).
You need to use & (ampersand) symbol added before variable argument in the function signature :
function mymod_form_alter(&$form, &$form_state, $form_id) {
# $form is referenced from the global scope using '&' symbol
$form['#validate'][] = 'mymod_user_profile_form_validate';
}

Related

Cakephp 3 - CRUD plugin - Use id from auth component

Currently, I'm using the CRUD v4 plugin for Cakephp 3. For the edit function in my user controller it is important that only a user itself can alter his or her credentials. I want to make this possible by inserting the user id from the authentication component. The following controller method:
public function edit($id = null){
$this->Crud->on('beforeSave', function(\Cake\Event\Event $event) {
$event->subject()->entity->id = $this->Auth->user('id');
});
return $this->Crud->execute();
}
How can I make sure I don't need to give the id through the url? The standard implementation requires the url give like this: http://domain.com/api/users/edit/1.json through PUT request. What I want to do is that a user can just fill in http://domain.com/api/users/edit.json and send a JSON body with it.
I already tried several things under which:
$id = null when the parameter is given, like in the example above. Without giving any id in the url this will throw a 404 error which is caused by the _notFound method in the FindMethodTrait.php
Use beforeFind instead of beforeSave. This doesn't work either since this isn't the appropriate method for the edit function.
Give just a random id which doesn't exist in the database. This will through a 404 error. I think this is the most significant sign (combined with point 1) that there is something wrong. Since I try to overwrite this value, the CRUD plugin doesn't allow me to do that in a way that my inserting value is just totally ignored (overwriting the $event->subject()->entity->id).
Try to access the method with PUT through http://domain.com/api/users.json. This will try to route the action to the index method.
Just a few checks: the controllerTrait is used in my AppController and the crud edit function is not disabled.
Does anyone know what I'm doing wrong here? Is this a bug?
I personally would use the controller authorize in the Auth component to prevent anyone from updating someone else's information. That way you do not have to change up the crud code. Something like this...
Add this line to config of the Auth component (which is probably in your AppController):
'authorize' => ['Controller']
Then, inside the app controller create a function called isAuthorized:
public function isAuthorized($user) {
return true;
}
Then, inside your UsersController you can override the isAuthorized function:
public function isAuthorized($user) {
// The owner of an article can edit and delete it
if (in_array($this->request->action, ['edit'])) {
$userId = (int)$this->request->params['pass'][0];
if ($user['id'] !== $userId) {
return false;
}
}
return parent::isAuthorized($user);
}

RejectChanges() doesn't undo changes when changes are made on a new entity?

I have a factory with that returns a new entity:
//myDataFactory
//some other code here...
function create() {
return manager.createEntity(entityName);
}
In my controller, when ever the backend returns a 404 (not found), I simply trigger that create() method when the controller is activated, otherwise I set the object to whatever was returned.
//myDataController
//some other code here...
init();
function init() {
var promises = [
getMyData()
];
console.log('populating MyData');
return q.all(promises).then(function(){
logger.info('populated MyData.');
});
}
function getMyData() {
return myDataFactory.getMyData(true).then(
setMyData,
createNewMyData
);
}
function setMyData(data) {
vm.myData = data[0];
}
function createNewMyData() {
vm.myData = myDataFactory.create();
}
In the UI, I have an undo button, that simply calls this function in the myDataController:
function undo() {
vm.myData.entityAspect.rejectChanges();
vm.termsForm.$setPristine(true);
}
The undo() function above works really well when there is an object returned from the database. But when I try making changes to a newly created entity which isn't saved yet, when I click undo, it's not going back to it's original value. Why? How do I resolve this? Any help would be greatly appreciated. :D
I am using angularjs with breeze btw.
"Original values" are the values that an existing entity had prior to your current, unsaved changes.
A new entity ... one not retrieved from the server and not yet saved ... lacks "original values". It only has "current values". Therefore, rejectChanges has nothing to roll back to.
There are other important differences as well. Look at the thing.entityAspect.entityState.name of a thing entity both before and after calling rejectChanges.
If the state before is "Modified", the state afterward is "Unchanged". But if the state before is "Added", the state afterward is "Detached" ... meaning that the thing is no longer in the EntityManager cache and is ripe for garbage collection.
With these facts in mind, what would you like the behavior to be? Once you know, we can work out a satisfying plan for your scenario.
For those stuck, here is my solution based on what I gathered from Mr. Ward's answer. I used entityState.isAdded() instead.
function undo() {
if (myDataIsNew()) {
resetMyData();
} else {
vm.myData.entityAspect.rejectChanges();
}
setPristineForm();
}
function myDataIsNew() {
// A new entity in cache that does not exist in the backend database
return vm.myData.entityAspect.entityState.isAdded();
}
function resetMyData() {
//clear each property.
}

cakephp 2.5.1: catching misspelled function calls

I have an API wirtten and gave out instructions how to us it.
For example to test the login you can call /api/login
Now I see in the logs, that someone keeps calling /API/LOGIN and gets an error 500.
Is there somehow a way, to catch such errors ONLY when calling /api/ controller functions?
In that case, I would like to send back a response like Error. Wrong function call.
I do not want to send this in general when a error 500 happens. Really only when /api/ related.
The error which gets reported belongs to the fact that I am calling in AppController::beforeFilter() a function like
$this->Api->check($username)
And I get this error when debug=2
Call to a member function check() on a non-object
When I call /api/login/ the functions works perfect.
I look forward for any tips! Thanks!
The problem you are facing isn't the casing of the action (in PHP method names are case insensitive), but the casing of the controller. It won't find APIController and therefore throw an missing controller exception. Your AppController is then being invoked as it is being extended by CakeErrorController which is used on errors.
I can only assume that $this->Api refers to a model, and since the actual controller is CakeErrorController, that model of course isn't being loaded, hence the non-object error.
There are various ways to solve this problem, personally I'd probably hook in to the Dispatcher.beforeDispatch event and throw an exception or define an appropriate response if necessary, something like this:
// app/Config/bootstrap.php
App::uses('CakeEvent', 'Event');
App::uses('CakeEventManager', 'Event');
CakeEventManager::instance()->attach(
function(CakeEvent $event) {
$controller = $event->data['request']->params['controller'];
$action = $event->data['request']->params['action'];
if(strtolower($controller) === 'api') {
$response = $event->data['response'];
if($controller !== 'api') {
$response->statusCode('403');
$response->body('Invalid controller message');
return $response;
}
if(strtolower($action) !== $action) {
$response->statusCode('403');
$response->body('Invalid action method');
return $response;
}
}
},
'Dispatcher.beforeDispatch',
array('priority' => 11)
);
This would enforce using lowercase for the controller and the action in case the api controller is being targeted.
However as already mentioned, method names are case insensitive, so forcing lowercase for actions isn't necessary from a technical point of view. Anyways, it's just an example...

cakephp: no save, no validate errors, no callbacks, empty sql log

In my controller i have this add function:
public function add(){
$this->layout = false;
if($this->request->is('post')){
$this->Paciente->create();
$this->Paciente->set($this->request->data);
if ($this->Paciente->validates()) {
if($this->Paciente->save()){
$this->set('status', '200');
} else {
// debug($this->Paciente->validationErrors);
// ini_set('memory_limit', '-1');
// var_dump($this->Paciente);
$this->set('status', '101');
// $log = $this->Paciente->getDataSource()->getLog(false, false);
// debug($log);
// die;
}
} else {
// didn't validate logic
// $errors = $this->Paciente->validationErrors;
// debug($errors);
$this->set('status', '100');
}
} else
$this->set('status', '404');
}
I'm sending the post info and the status is always 101. As you can see I have tried a lot to find out where is the error, but no luck.
Also I don't have any callback in my Model (beforeSave, afterSave...)...
Anyone knows what is happening?
Your
if ($this->Paciente->validates()) {
if($this->Paciente->save()) {
}
is wrong.
You are validating twice.
Please - as documented - either use false (save(null, false)) to not invalidate twice or simply remove the validates() part here.
You can directly do:
if ($this->Paciente->save($this->request->data)) {}
This itself is unlikely causing your issue, though.
Or do you have some custom validation rules that on second (and faulty) trigger will make a field invalidate?
You can confirm that by checking $this->Paciente->validationErrors afterwards. It should be empty. If it does not save (and it's for sure no callback), and if it does not throw exceptions due to SQL errors then it most likely are your validation rules.
The problem was in the way i was sending the post info:
I was setting the variables as data['Model']['var'] and the correct way is data[Model][var]
Thanks #mark, #AD7six, #liyakat and #Alex for your help in the problem.
I can't imagine. When you set your array values like above it would likely break in php5.3 >

How to properly exit from a custom blackhole handler?

My problem is fairly basic: whenever an action in CakePHP is blackholed, I want to display a custom error page; the default behaviour of Cake to display a "File not found" message confuses the hell out of users (not to mention developers). So I came up with this, by searching the docs and StackOverflow:
class TestsController extends AppController
{
public $components = array ('Security');
public function beforeFilter ()
{
parent::beforeFilter ();
$this->Security->blackHoleCallback = 'blackhole';
$this->Security->csrfExpires = '+5 seconds'; // for testing
}
public function index ()
{
}
public function doit ()
{
$this->log ('Performing request; entry = ' . $this->data['foo'], 'tests');
$this->set ('foo', $this->data['foo']);
}
public function blackhole ($type)
{
$this->log ('Request has been blackholed: ' . $type, 'tests');
$this->render ('/Errors/blackhole');
$this->response->send ();
exit ();
}
}
In index.ctp there is a simple form with a single textbox, that commits to doit (excluded for brevity). This works, but I have one major issue: the exit() in the blackhole() function. The problem is, if I do not exit here doit() is still called, even if the request is blackholed, as evidenced by the log:
2013-01-30 15:37:21 Tests: Request has been blackholed: csrf
2013-01-30 15:37:21 Tests: Performing request; entry = kfkfkfkf
This is clearly not what you expect. The Cake documentation hints at using (custom) exceptions to stop processing in blackhole(), but that:
completely beats the purpose of using a custom handler;
adds another layer of complexity to something that should be simple.
My question is: is there a proper way to do an "exit" from blackhole() so that Cake does all the rendering/cleanup/etc. that it usually does; I already had to add $this->response->send() to force output to the browser. Or, althernatively, a way to tell Cake to skip calling doit() after blackhole().
My suggestion would be to redirect in your blackhole.
This is done e.g. here in the cookbook http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#usage
$this->redirect(array('controller' => 'test', 'action' => 'index'));
A redirect will issue an exit (http://book.cakephp.org/2.0/en/controllers.html#flow-control).
You can also send something nice to the user if you want, before the redirect:
$this->Session->setFlash('What are you doing!?');
In your blackhole callback you could just throw an exception with required message. That would render the proper error page and get logged too (assuming you have turned on logging for errors in core.php).

Resources