Drupal 'Send Email' advanced action - drupal

I may be missing something blindingly obvious here (I hope so).....I am creating a module in Drupal 6 that consists of some triggers and actions. in it's simplest form it consists of:
An action which checks for some criteria (event that needs to be triggered once a month per user)
A trigger which is fired for each user that the criteria is true for
I would like as much as possible to be managed through the triggers / actions interface in Drupal as the site admin is not a developer. The plan is to use the cron trigger to fire the action in 1. which will then fire a trigger for each user. The site admin will then be able to create a Send Email action through the actions interface and hook it up to the trigger from 2.
The part I can't get my head around is how the recipient of the email will be specified - the user trigger will be fired from an action run by cron (i.e. not in any user context) - how can I pass in a variable that can be used here?
Thanks,

Triggers fire actions not the other way around.
The user that you pass to actions_do dosn't have to be the logged in user. You can query for the users that you want to email and loop thrhough them doing user_load and then an actions_do
something like
foreach ($user_ids as $uid) {
$context_user = user_load(array('uid' => $uid));
$context = array(
'hook' => 'myhook',
'op' => $op,
'user' => $context_user,
);
actions_do(array_keys($aids), $context_user, $context);
}

Related

Delete Firebase anonymous users after a while

I am using anonymous auth to allow my users to use the app without logging in. However, Firebase seems to persist these anonymous user IDs indefinitely. Is there a way to automatically purge these or set some sort of expiration rule? I don't want these one-time use IDs to live forever and clutter the actual user data from providers.
Unfortunately this is a "memory leak" (user leak?) Since there is no reasonable way to force an anonymous user to convert, these anonymous user ids will soon become zombies that serve no particular purpose (that I can think of). Furthermore, a single real user might (forgetfully) sign in as an anonymous user, yet again, after already linking their email to a previous incarnation, and then get frustrated when trying to link to their email. Overall, I find the current anonymous user implementation impractical, or at very least far from ideal.
For now I am planning to have an email address which is random but unique for a given user/device for signing in anonymous users, instead of using the builtin anonymous signin (which is disabled). In my opinion there needs to be a setting to tell Firebase to delete an anonymous user id upon sign out (they are useless at that point anyway) and/or after a predefined amount of time. In addition, it might be useful to be able to sign in again, with the same anonymous user id, until the expiration time (like by saving a token/etc.) Lastly, an attempt to link an email that is already in use should just merge the anonymous user id with the existing email/password user id through a verification step.
Somehow, there is a way of delete old anonymous users. I do it with a AppEngine cronjob that runs hourly.
But before you do that you have to define, what a anonymous user is. My users have to validate their email address and therefore I declare all users who are not validated to be anonymously after 90 days.
With the PubSub tick I then collect all users and delete them, here you've got a sample:
export const removeOldUsers = functions.pubsub.topic( "hourly-tick" ).onPublish( event => {
function getInactiveUsers( users: Array<UserRecord> = [], nextPageToken?: string ) {
let userList = users;
return admin.auth().listUsers( 1000, nextPageToken ).then( ( result: any ) => {
console.log( `Found ${result.users.length} users` );
const inactiveUsers = result.users.filter( ( user ) => {
return moment( user.metadata.lastSignInTime ).isBefore( moment().subtract( 90, "days" ) ) && !user.emailVerified;
} );
console.log( `Found ${inactiveUsers.length} inactive users` );
// Concat with list of previously found inactive users if there was more than 1000 users.
userList = userList.concat( inactiveUsers );
// If there are more users to fetch we fetch them.
if ( result.pageToken) {
return getInactiveUsers( userList, result.pageToken );
}
return userList;
} );
}
return new Promise( ( resolve ) => {
console.info( `Start deleting user accounts` );
getInactiveUsers().then( ( users ) => {
resolve( users );
} );
} ).then( ( users: Array<UserRecord> ) => {
console.info( `Start deleting ${users.length} user accounts` );
return Promise.map( users, ( user ) => {
return admin.auth().deleteUser( user.uid ).then( () => {
console.log( "Deleted user account", user.uid, "because of inactivity" );
} ).catch( ( error ) => {
console.error( "Deletion of inactive user account", user.uid, "failed:", error );
} );
}, { concurrency: 3 } );
} ).then( () => {
console.info( `Done deleting user accounts` );
} );
} );
Here I just pushed my class to npmjs #beyond-agentur-ug/firebase-delete-inactive-users
There is no way to bulk-delete, however, the following trick worked for me:
I used Macro Recorder and it worked like a charm. Just recorded a few iterations in the console of me deleting users, set it to repeat 500 times and walked away.
You can use Firebase's admin API to delete users programatically. You'll need to store a user list in your database as Firebase doesn't provide a query for that.
Managing users
Anonymous users can be a starting point before you upgrade them to a non anonymous user (think of an e-commerce site where an anonymous user adds stuff into his cart and then on checkout, upgrades to a Google or email/password user; in this case you probably do not want to lose the user's cart). As explained, this could be useful if you want to persist data from an anonymous user to an upgraded user. If you wish to purge anonymous users, there is no automated way to do so. However as soon as you either sign out the anonymous user or sign in a non anonymous user, the state of the anonymous user will be lost.

Change current user role with form selection on update (not entry creation)

I'm using Formidable forms in Wordpress and have a form that registers users. I can use a radio button in the registration form to determine what their role will be. I have a hook for that. What I need, however, is a hook that will change the user role based on radio selection on form entry UPDATE. My current code only works on entry creation. Here is the code that assigns roles on registration:
add_filter('frmreg_new_role', 'frmreg_new_role', 10, 2);
function frmreg_new_role($role, $atts){
extract($atts);
if($form->id == 8){
if($_POST['item_meta'][280] == 'Job Applicant')
$role = 'applicant';
}
return $role;
}
"8" is the id of the form itself. "280" is the id of the radio button field where "Job Applicant" is one of the values. And "applicant" is one of our site's user roles.
I need an adaptation of this that will change the role after the entry has already been created, on update. The closest thing I can find is a hook that changes user role after a successful PayPal payment. I tried to combine the two but I couldn't get it to work. Here is the PayPal generated user role changer:
add_action('frm_payment_paypal_ipn', 'change_paid_user_role');
function change_paid_user_role($args){
$new_role = 'contributor'; //change this to the role paid users should have
if(!$args['pay_vars']['completed'])
return; //don't continue if the payment was not completed
if(!$args['entry']->user_id or !is_numeric($args['entry']->user_id))
return; //don't continue if not linked to a user
$user = get_userdata($args['entry']->user_id);
if(!$user)
return; //don't continue if user doesn't exist
$updated_user = (array)$user;
// Get the highest/primary role for this user
$user_roles = $user->roles;
$user_role = array_shift($user_roles);
if ( $user_role == 'administrator' )
return; //make sure we don't downgrade any admins
$updated_user['role'] = $new_role;
wp_update_user($updated_user);
}
UPDATE: the action hook should probably be: frm_after_create_entry according to Formidable forums.
Many times, researching the core files is more productive than any Google or Manual. Dropping the whole plugin directory in a code editor and researching for the string frm_after_create_entry takes us to the create() method where this hook happens.
After that, there's the update() method and it provides the action hook: frm_after_update_entry.
This hook passes two parameters: $id and $new_values['form_id']. I cannot reproduce your setup, so testing the hook is up to you.
Reference: Actions and filters are NOT the same thing…
In this example:
add_action( 'frm_after_update_entry', 'change_role_to_staff', 10, 2);
function change_role_to_staff( $form_id, $values ){
var_dump($values);
die();
}
As this is an action hook, nothing has to be returned.
There's no $roles or $atts, the parameters are the form ID and Values.
What you're looking for is inside $values.
var_dump() and die() are for debugging purposes and must be removed at once after testing.
Do your wp_update_user with this values and adapting your previous code.

Drupal login error message displayed without using form_set_error()

I created a module that adds a field to the user login block form to display errors. I want to display the error in the new field instead of using form_set_error(). I am able to see the warning field in the login block. But when I submit with an error it does not display the error.
Code is as follows. I do not understand how to refresh the value of form once it gets an error.
function usermoved_form_user_login_block_alter(&$form, &$form_state) {
$form['warning'] = array(
'#value' => t('oops'),
'#weight' => 11
);
$form['#submit'][] = 'usermoved_form_submit_code';
}
function usermoved_form_submit_code($form, &$form_state) {
global $user;
if (!$user->uid) {
$form['warning']['value']= "changed to someting";
}
}
Your code is wrong: Instead of using $form['warning']['value'], it should use $form['warning']['#value']. Even doing so, the message (which should be passed to t()) is not shown to the users because $form is not passed by reference, and it is not the value returned from the form submission handler, which is not expected to return any value.
Form submission handlers are then never called, when a form validation handler raises an error. If you are checking the user ID to see if the user has not been logged-in, then you cannot do it in a form submission handler, as the user not being logged-in means there have been errors during the validation phase, and the form submission handlers are not invoked.
What you can use is a form validation handler.
function usermoved_form_user_login_block_alter(&$form, &$form_state) {
$message = (empty($_SESSION['usermoved_form_user_login_block_alter']) ? t('Initial message') : $_SESSION['usermoved_form_user_login_block_alter']);
$form['warning'] = array(
'#value' => $message,
'#weight' => 11
);
$form['#validate'][] = 'usermoved_form_validate_code';
}
function usermoved_form_validate_code($form, &$form_state) {
if (empty($form_state['uid'])) {
$_SESSION['usermoved_form_user_login_block_alter'] = t('The error message');
}
else {
unset($_SESSION['usermoved_form_user_login_block_alter']);
}
}
$form_state['uid'] is a value set from user_login_authenticate_validate(), the second validation handler invoked by Drupal. When it is not set, it means there have been some errors before Drupal tried to authenticate the user, or the user didn't authenticate. The first case can happen when the user is blocked (user_login_name_validate() verifies that), or the user tried too much times to authenticate (see user_login_authenticate_validate() which uses flood_is_allowed() to verify the user didn't try too much times to authenticate); the latter case can happen when the user didn't enter the right password. (See user_login_final_validate().)
As side notes:
I used t('Initial message') as default message, but that could be replaced with '', if you don't have any message to show to the users before they log in.
The user login block is not shown to already logged-in users. $user->uid (where $user is the global variable) is always 0, when the login block is shown, and when logging-in failed; checking the value of $user->uid while showing the login block doesn't make sense.

Bypass Node Delete Confirm Form in Drupal

Looking for the best way to allow users to delete nodes on a site without the need to use a confirm form. I have tried using a form_alter to direct people to a custom submit function, without success.
Anyone ever tried this?
Assuming drupal 7, the node/%/delete menu entry is wired directly to the node_delete_confirm form. You can modify it with with a hook_menu_alter, and change the function from drupal_get_form to a page callback of your own design that will just delete the node.
Example:
In your module file you'd need:
function mymodule_menu_alter(&$items) {
$items['node/%node/delete']['page callback'] = 'my_node_delete_function';
$items['node/%node/delete']['page arguments'] = array(1);
$items['node/%node/delete']['module'] = 'mymodule';
$items['node/%node/delete']['file'] = 'mymodule.pages.inc';
}
And in your mymodule.pages.inc file you'd need:
function my_node_delete_function($node) {
// Taken from node modules node_delete_confirm submit handler
node_delete($node->nid);
watchdog('content', '#type: deleted %title.', array('#type' => $node->type, '%title' => $node->title));
drupal_set_message(t('#type %title has been deleted.', array('#type' => node_type_get_name($node), '%title' => $node->title)));
// Do a drupal goto here to preserver the 'destination' parameter
drupal_goto();
}

Hooking user registration in Drupal

I have a site where some users will be registered by our staff, and won't have emails associated with them. I would like to keep the email field a required field, so I devised a random email generator.
function generateRandomEmail() {
$email = 'noemail'. rand(0,1000000) . '#noemail.com';
return $email;
}
So, I attached that to the user register form alter, and it worked nicely, effectively generating an email for these users.
However, in the process, all the other fields associated with the main account section (password, username, notify, etc.) disappeared. My question, is there a quick way to populate the rest of the fields that I don't want to alter? I've used drupal_render($form); in a tpl.php, but it didn't work in the form alter.
Here is where I'm altering the form:
function accountselect_user($op, &$edit, &$account, $category) {
if ($op == 'register') {
$fields['account']['mail'] = array(
'#type' => 'textfield',
'#default_value' => generateRandomEmail(),
);
You are currently using hook_user for your manipulation, but that is the wrong place. On $op 'registration', you can return additional fields you want to inject to the registration process, but not alter existing fields. Use hook_form_alter() or hook_form_FORM_ID_alter() for that, e.g.:
function yourModule_form_user_register_alter(&$form, &$form_state) {
$form['account']['mail']['#default_value'] = generateRandomEmail();
}
You probably want to add a check that the request is in fact coming from the staff, since the above code would prepopulate the email field for the normal registration form also!
Also, please do not generate 'random' mail addresses using existing third party domains (like 'nomail.com'). Use the reserved 'example.com', or better yet, one that you own yourself!

Resources