Adding to flash message on new user save

I’m trying to figure out a way to let admins easily copy the activation link (i.e. UsersActivations->url) after they add new users. Ideally, this would look something like the screenshot below.

This way, the admin can send a personal email, which is less likely to end up in a junk folder. I like the idea of adding it to the flash message since it makes for a simple workflow, but I could also see adding this option for any existing users who have not been activated, as shown below.

Screenshot 2022-11-23 at 1.13.47 PM

So my question is whether filtering or appending to that flash message is supported or even possible to do cleanly using the API?

I think I could accomplish this in a messy way using the admin_users_browse hook, just looping through all the users and appending content using regex matching and some JavaScript. But that feels a little gross.

Does anyone have ideas for the best way to accomplish the general goal (if not the specifics)?

(PS: I would need to do this in a plugin; editing core is not helpful for my case)

A little progress, I found that I can add a message like so, but that it doesn’t accept HTML.

public function hookAdminUsersBrowse($args){
    if($somecondition = true){
        $message = __('lorem ipsum <a href="">this is not a link</a>');
        $flash = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger');
        $flash->addMessage($message, 'success');

Also, as far as I can see, there’s no data available via $args that says which user was just added.

I would consider using $flash->getCurrentMessages() and/or $flash->hasMessages() but I’m not sure how to do so. Those seem to be returning an empty array or false, respectively, so I guess I’m doing something wrong.

I ended up abandoning the flash() approach (for now) and just added new links to the actions list for inactive users.

<?php // via admin_footer hook
	$copyLink=__('Copy Activation Link');
	$inactive_users_helper = true; // @todo: plugin option
	if(is_current_url('/admin/users') && $inactive_users_helper){
		$protocol = $_SERVER['HTTPS'] == 'on' ? 'https' : 'http';
		$host = $_SERVER['HTTP_HOST'];
		$ua = get_records('UsersActivations');
		$user_activations = array();
		foreach($ua as $user){
			$user_activations[] = array('id'=>$user['user_id'],'url'=>$protocol.'://'.$host.'/admin/users/activate?u='.$user['url']);
		$inactive_users = json_encode($user_activations);
	// add links to copy user activation links for inactive users
	var inactive_users = <?php echo $inactive_users ? $inactive_users : json_encode([]);?>;
			let user_delete_button = jQuery('li a[href="/admin/users/delete-confirm/'+user['id']+'"]');
				user_delete_button = user_delete_button.parent();
				let li = document.createElement('li');
				let confirmation = document.createElement('i');
				confirmation.setAttribute('class','fa fa-check-circle'); // font awesome
				confirmation.setAttribute('title','<?php echo $copied;?>'); = 0; = '.25em';
				let link = document.createElement('a');
				link.innerText = '<?php echo $copyLink;?>';
					if(navigator && navigator.clipboard && navigator.clipboard.writeText){
						navigator.clipboard.writeText(; = 'none'; = 1;
						setTimeout(()=>{ = 'opacity .15s linear' = 0;

I now see some weirdness in the results that I missed earlier on my dev site. On an actual production site (which goes back to 2011), it looks like the users_activations table stores rows both for users that have been deleted and also for users that have already been activated. It also stores multiple rows for a single user_id for some reason.

I modified the code a little to account for the duplicates and the already active users.


1: get only the most recent activation url for each inactive user

	$copyLink=__('Copy Activation Link');
	$inactive_users_helper = get_option('my_plugin_option'); 
	if(is_current_url('/admin/users') && $inactive_users_helper){
		$protocol = $_SERVER['HTTPS'] == 'on' ? 'https' : 'http';
		$host = $_SERVER['HTTP_HOST'];
		$user_activations = array();
		$u = get_records('User',array(
		foreach($u as $user){
			$ua = get_record('UsersActivations',array(
				$user_activations[] = array('id'=>$user['id'],'url'=>$protocol.'://'.$host.'/admin/users/activate?u='.$ua['url']);
		$inactive_users = json_encode($user_activations);

2: Select only users with the inactive class

let user_delete_button = jQuery('.inactive li a[href="/admin/users/delete-confirm/'+user['id']+'"]');

Remaining question

The odd thing is that now I have a bunch of inactive users without activation links. Can someone explain why this is? Do old activation links get removed? If so, how and why does that not always happen (as with the active users found in the table)? Is this something that was fixed long ago and the age of my site is just throwing off the expected results?

Not all active users have activation URLs

Activation records get deleted when the user successfully uses them, and on some other actions like when a user is manually activated or deactivated.

As for inactive users that don’t have an activation record, you could end up like that if the user was manually marked inactive.

I think I was thrown most by the activation records for active users and the presence of multiple records for the same user. In the end, it was easy to work around, but I wonder if there was a bug at some point.

The user activation system is also used for forgotten passwords, so it doesn’t align 1:1 with active/inactiveness.

Multiple, the database setup allows for it, but generally issuance of new activation codes should delete an existing one. Probably you’re seeing the result of the site being as old as it is there, from when that wasn’t done perhaps.

1 Like