Drupal: How to differentiate between new user registration and user password update in hook_user validate operation? - drupal-6

I am using hook_user validate operation to validate user registration info against my business logic.
I want separate logics to run on registration and change password.
But I am unable to differentiate between the two - both pass through validation and same code is run for both.
How can I differentiate between the two inside the validate op in hook_user?

with $form_id
if ( ($form_id == 'user_profile_form' && arg(3) == NULL) {
// validation code for updating
}
elseif ($form_id == 'user_register') ) {
// validation code for registering
}

In Drupal 7 you may try something like:
/**
* Implements hook_form_FORM_ID_alter().
* Form ID: user_profile_form
*/
function foo_form_user_profile_form_alter($form, &$form_state) {
// Set a custom form validate and submit handlers.
$form['#validate'][] = 'foo_form_user_profile_form_validate';
$form['#submit'][] = 'foo_form_user_profile_form_submit';
}
/**
* Implements hook_form_FORM_ID_alter().
* Form ID: user_register_form
*/
function foo_form_user_register_form_alter($form, &$form_state) {
if ($form['#user_category'] == 'account') {
// Set a custom form validate and submit handlers.
$form['#validate'][] = 'foo_form_user_register_validate';
$form['#submit'][] = 'foo_form_user_register_submit';
}
}

Related

MS Graph API returns 500 when I try to request groups

So I am developing a sharepoint webpart where I need to check if a user is in an AD group. Now here is the odd part the MS Graph query I need to you for that is:
https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true
And that is the exact Query that my WP sends out, but it returns a 500 error message. Now I thought I had permissions missing, but the Explorer says I do not need any.
Here is my GraphService that handles MS Graph:
import { MSGraphClient } from '#microsoft/sp-http';
import * as MicrosoftGraph from '#microsoft/microsoft-graph-types';
import { WebPartContext } from "#microsoft/sp-webpart-base";
/**
* The class that handles the MS Graph API calls
*/
export default class GraphService {
/**
* The MS Graph client with does the calls
*/
private _client:MSGraphClient;
/**
* Sets the client for the Graph API, needs to be called before the class can be used
* #param context The context of the WP
*/
public async setClient(context:WebPartContext) {
this._client = await context.msGraphClientFactory.getClient();
}
/**
* Checks to see if a user belongs in a user group or not
* #param groupName The group name with we want to check
*/
public async checkCurrentUserGroup(groupName:string) {
const rawData = await this._client.api('/me/transitiveMemberOf/microsoft.graph.group').count(true).get();
const arr = rawData.value.filter(i => i.displayName == groupName);
return !!arr.length;
}
/**
* Returns the users from AD who have a manager and whom companyName is a specific company name
* #param companyName The company name to whom the users need to belong to
*/
public async getUsers(companyName:string):Promise<any[]> {
const rawData = await this._client.api('/users').filter('accountEnabled eq true').expand('manager').select('displayName,companyName,mail,department,jobTitle').get();
//need to do manual filtering, becuase you can't user filter on the "companyName" field and you can't search and expand at the same time without returing everything
const filteredData = rawData.value.filter(i => i.companyName == companyName && i.manager);
//remaps the data to IUserModel
const ret:any[] = filteredData.map(i => <any>{
name: i.displayName,
id: 0,
email: i.mail,
manager: i.manager.displayName,
managerEmail: i.manager.mail,
managerId: 0,
hasDiscussionForm: false,
department: i.department,
position: i.jobTitle
});
return ret;
}
}
The problem then is that the method checkCurrentUserGroup returns 500.
I've given the following permissions to the wp:
Group.Read.All
Directory.Read.All
User.Read
The getUsers method works as expected.
In the MS Graph Explorer the query works just fine. What am I missing?
According to the document the request header must have ConsistencyLevel and eventual header and please make sure to pass the request header and please refer to this document for more information
let res = await client.api('/users/{id}/transitiveMemberOf/microsoft.graph.group')
.header('ConsistencyLevel','eventual')
.search('displayName:tier')
.select('displayName,id')
.orderby('displayName ')
.get();

Webform - broken link when trying to pass values

I've got a custom module that takes the email address from a form and inserts a link in the email handler (via a token) that when clicked on goes to another webform and when submitted sends the reply to the original email address.
What I'm trying to do is pass the submitted values from the first form in to this link but it's not working:
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_alter() for webform.
*
* Add the custom validation handler to the 'webform-1'.
*/
function webformcustom_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if ($form_id == 'webform_submission_get_offers_add_form') {
$form['#validate'][] = 'webformcustom_validator';
}
}
/**
* The custom validation handler.
*
* Get an email of a user, encrypt it and insert it
* into the body of the notification email.
* Generate a random token and store it with a user's email as a 'key'/'value'
* using State API.
* A token that is added to the link on the webform_2 as the argument,
* like this: http://drupal8.dev/form/webform-2?token=fb41566252aec769.
* This token then will be using in the hook_preprocess_HOOK() for extraction
* of a user's email.
*/
function webformcustom_validator(&$form, FormStateInterface $form_state) {
$email = $form_state->getValue('email_address');
$webform = \Drupal::entityTypeManager()
->getStorage('webform')
->load('get_offers');
$email_handler = $webform->getHandler('email');
$configuration = $email_handler->getConfiguration();
// Generate a random token.
$token = bin2hex(openssl_random_pseudo_bytes(8));
\Drupal::state()->set('webformcustom_' . $token, $email);
$url = \Drupal::urlGenerator()->generateFromRoute('<front>', [], ['absolute' => TRUE]);
$url .= 'provide-a-quote/?select_a_car=[webform-submission:values:select_a_car]&token=' . $token;
// Insert the link to the 'webform-2' with the token into the email body.
$configuration['settings']['body'] = '<a href=' . $url . '>Submit offer</a>';
$email_handler->setConfiguration($configuration);
}
/**
* Implements hook_preprocess_HOOK() for webform templates.
*
* Read the token from the url and extract related a user's email.
* Then set a user's email in to the email handler configuration.
* This enables to notify a user when the 'webform_2' will be submit.
*/
function webformcustom_preprocess_webform(&$variables) {
if ($variables['element']['#webform_id'] == 'provide_a_quote') {
$token = \Drupal::request()->query->get('token');
$email = \Drupal::state()->get('webformcustom_' . $token);
$webform = \Drupal::entityTypeManager()
->getStorage('webform')
->load('provide_a_quote');
$email_handler = $webform->getHandler('email');
$configuration = $email_handler->getConfiguration();
$configuration['settings']['to_mail'] = $email;
$email_handler->setConfiguration($configuration);
$webform->save();
}
}
What it gives me as a link on the email is a broken link:
Audi?token=01f89ea29aa5251f>The link to the webfrom-2

How to display flash message in Kohana 3

I have to show message after insert some data in database. I'm using Kohana. Is there a way to do that with flash messages? It's better than header refresh.
Well sort of. You could use the Session::get_once() function. But this only let you retrieve a variable once, and you cannot use it again in the same request. While you want a flash message to persist a full request cycle. To manage that you'll need a wrapper class, something like this.
class Flash {
private $session;
private $messages = array();
private static $_instance; // Singleton object
public static function instance() {
if ( ! isset( self::$_instance ) ) {
self::$_instance = new Flash();
}
return self::$_instance;
}
private function __construct() {
$this->session = Session::instance();
$this->messages['current'] = $this->session->get_once('flash');
if( ! is_array($this->messages['current'] ) ) {
$this->messages['current'] = array();
}
}
public function add( $key, $message === null ) {
if ( is_null( $message ) ) {
$message = $key;
$key = null;
}
$this->messages['new'][$key] = $message;
$this->session->set('flash', $this->messages['new'] );
return true;
}
public function get( $item = null ) {
if( $item === null ) {
return $this->messages['current'];
}
if( ! array_key_exists($item, $this->messages['current']) ) {
return null;
}
return $this->messages['current'][$item];
}
}
Usage:
$flash = Flash::instance();
$flash->add('A random message');
$flash->add('some_key', 'Some message');
$flash->get(); // array( 0 => 'A random message', 'some_key' => 'Some message')
$flash->get('some_key'); // 'A Random message'
What it does basically is on initialization it retrieves the current message from the session, using the get_once() function. The variable is nou out of the Session object, so it will only last this request. Everytime you add a variable, it will immediately persisted to the Session object.
There is just one problem; if you are using ajax calls, the messages will only be available on the initial php request, not on subsequent ajax calls. And there is also no restriction whatsoever on what kind of variable you are storing (but it must be serializable). You'll have to build in some checks for that too.
warning: the class is not tested, so it would surprise me if you do not get a syntax error ;)
And to go a step further: you would need an extra refresh anyway. The request flow should be like this imo:
Request 1: User is presented form
Request 2: User posts the form, which is processed. Data is inserted in database. When done, user is redirected
Request 3: A confirmation page is shown (can be "thank you", or the detail page, whatever).
You would set the flash message in request 2, and show it in 3. I would not directly show the thank you page on request 2, because when the user refreshes, the form will be posted again.
Use this module. Works perfectly :)

Symfony2 Entity form type with a specific query_buider

Context
In my case, I've some orders with "discount vouchers" (discount). A discount can be use on under different conditions. For instance, discounts have an expired date, can be used by a limited number of customers, can be dedicated to a user, ...
Each discount can be attached to several order.
In my backoffice, I want to add to order create form a field "Discount" with a list of discount available but only right discounts.
What I made
An entity "order" with a field manyToMany
/**
* #ORM\ManyToMany(targetEntity="PATH\MyBundle\Entity\Discount", inversedBy="orders")
* #ORM\JoinTable(name="shop_discounts_orders",
* joinColumns={#ORM\JoinColumn(name="order_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="discount_id", referencedColumnName="id")}
* )
*/
private $discounts;
An entity "discounts" with a field manyToMany
/**
* #ORM\ManyToMany(targetEntity="PATH\MyBundle\Entity\Order", mappedBy="discounts")
*/
private $orders;
A form OrderType with a field discounts
$builder->add('discounts', 'entity',
array( 'label' => 'Discount vouchers',
'required' => false,
'expanded' => true,
'class' => 'PATH\MyBundle\Entity\Discount',
'property' => 'title',
'multiple' => true,
'query_builder' => function(EntityRepository $er) use ($params) {
return $er->getQuerySelectType($params);
},
));
With this solution, I can return specific discount defined by my request in my entity repository. It's good for expired date condition for instance.
What I would like
I'd like to filter results in the checkbox list. In fact, I want limit usage of the discount to a dedicated user, limit to a list of products, or limit the number of usage... And these condition cannot be done by a simple sql request.
I try to create special Type. My idea is to have an array of entities Discount and load a choice list... After that, I create a dataTransformer but It doesn't work !
Thank's for your ideas !
You could use the $options from public function buildForm(FormBuilderInterface $builder, array $options) to pass your user and product for instance. With those 2 informations you could refine your list of discount (in your query)
if you do so you need to add them in the setDefaultValue
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'user_discount' => null,
'product_discount' => null,
));
}
and in your controller:
$form = $this->formFactory->create(new YourFormType(), $entity, array(
'user_discount' => $this->getUser(),
'product_discount' => $product,
));
I found a solution and explain it if someone have the same issue as me.
Create a custom Type
My custom type is inspired by Symfony\Bridge\Doctrine\Form\Type\DoctrineType
class DiscountOrderType extends AbstractType
{
// overide choiceList callback
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$choiceListCache =& $this->choiceListCache;
$type = $this;
$choiceList = function (Options $options) use (&$choiceListCache, &$time, $container) {
[[ Copy paste same as Doctrine type ]]
// Create your own choiceList class (EntityChoiceList)
if (!isset($choiceListCache[$hash])) {
$choiceListCache[$hash] = new DiscountChoiceList(
$options['em'],
$options['class'],
$options['property'],
$options['loader'],
$options['choices'],
$options['group_by']
);
// If you want add container
$choiceListCache[$hash]->setContainer($container);
}
return $choiceListCache[$hash];
};
$resolver->setDefaults(array(
'choice_list' => $choiceList,
));
}
Create a custom EntityChoiceList
My custom type is inspired by Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList
class EntityChoiceList extends ObjectChoiceList
{
protected function load()
{
if ($this->entityLoader) {
$entities = $this->entityLoader->getEntities();
} else {
$entities = $this->em->getRepository($this->class)->findAll();
}
// You have access to the entities in the choice list
// Add your custom code here to manipulate the choice list
// you can do some check not properly possible with sql request (http requests on each result, ...) before add it in choice list
// you can add some custom cache rules, ...
// if you use gedmon and want apply a "join" with translate table, you can add $query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'); before playing request...
// Possibilities are infinite
// FOR INSTANCE : you already want unset first entity of the result
if (isset($entities[0])) {
unset($entities[0]);
}
// END OF CUSTOM CODE
try {
// The second parameter $labels is ignored by ObjectChoiceList
// The third parameter $preferredChoices is currently not supported
parent::initialize($entities, array(), array());
} catch (StringCastException $e) {
throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e);
}
$this->loaded = true;
}
Of course you can try to extend symfony class for beautyfull code ;).
Thank's to #maxwell2022 for your help !

drupal 6: write to 2 different tables for user account registration form

function Mymodule_user($op,&$edit, &$account, $category = NULL) {
switch ($op) {
case 'register':
$result = db_query("SELECT id,name FROM {sites} ORDER BY name");
while($row=db_fetch_array($result)) {
$sites[$row['id']] = $row['name'];
}
$form['site_select'] = array(
'#type' => 'select',
'#title' => t('Select your site'),
'#options' => $sites,
)
return $form;
case 'insert':
//How to take the $form values from above and use in my query to
//write to my own table while writing to the standard 'users' table?
db_query("INSERT INTO {another_table} (site_name) VALUES ('%s')",
$form['site_select']);
);
When the user hits the SUBMIT button when creating a standard Drupal account, how do I pass my custom field value of $form['site_select'] to my case 'insert' so that I can write this to 'another_table'. The regular user data such as username and password needs to continue to write to the default 'users' table.
Q: Why not just let Drupal serialize and save your data to the 'users' table in the 'data' field as it normally does?
A: Because I want to be able to AJAX-ify and use autocomplete in another Drupal form, as well as query specific custom fields in MySQL. MySQL cannot serialize/unserialize. For example, "SELECT DISTINCT site_name FROM another_table"
You're probably gonna want to alter the user_register form in order to add your own callback function to that form's #submit property, like:
<?php
/**
* Implementation of hook_form_FORMID_alter().
* #param $form
* #param $form_state
* #return void
*/
function MYMODULE_form_user_register_alter(&$form, &$form_state) {
$form['#submit'][] = 'MYMODULEs_own_register_submit_callback_func';
}
And then in the callback you're going to have $form_state filled with whatever the user filled into the form (including the value for the extra site_select field element which you added in your hook_user implementation):
<?php
function MYMODULEs_own_register_submit_callback_func(&$form, &$form_state) {
// Do stuff with $form_state['values'], i.e $form_state['values']['site_select'], etc.
}

Resources