Does anyone have group emails working for users?

I’ve been combing through old posts all day on this topic, reviewing the code and the database tables trying to figure out what’s going on without any luck (or direction to go in!). Thought it might be a good idea to start a new thread rather than bump a bunch of old ones.

I can add group email accounts, I can see them as administrator but can’t assign them to users properly or have the users be able to see the group inbox folders.

Does anyone have this working? If so, please help! I’ve seen all the existing threads and also the bug report on Github. Kind of looking for something new if anyone has insight.

Here’s what I’ve found so far…

I can confirm there is still an issue here. 7.12.8. I’ve done a bunch of testing as well. What I’ve found so far…

  1. My folders_subscription is getting populated properly in the DB
  2. An older group email that I added has no issues.
  3. A recent group email I can’t have users see it. (Admin works no problem).
  4. I can add the folder subscriptions by logging in as admin to the user. (However, the popup says “saved” but they are never saved, if I re-open the user, they are gone).
  5. The folder subscriptions are not available for the user when logged in as the user!!!

I’ve added a video here demonstrating what’s happening.

I’ve tried the usual stuff like file permissions/ownership and examining the folder_subscription table in the Database (which looks like it’s properly associating the folder with the user.

The only errors I get are about duplicate folders being detected. I’ve checked, there are no duplicate folders. I think when I save, it’s trying to re-add the folders that are already in the table, and thus the error. Which I don’t think is a problem, because the folder ARE in the table.

2 Likes

I found this screen shot from SugarCRM from 9 years ago. Wondering if its from CE? It appears some functionality is missing in SuiteCRM that was there to select the team the group email was assigned to.

Anyone know the history on this and how we got to where we are now?

“Teams” were SugarCRM Pro features, if I’m not mistaken.

I think that Group Emails should work in SuiteCRM, though these configurations are always a bit fragile. I would recommend trying things out with a really simple email account, nothing Gmail or Hotmail or Azure, nothing OAuth or whatever. Just plain user/password account. I use gmx.com emails for all my tests.

Another thing that you might want to check is how things are saved in the DB. Double-check everything in inbound_email, outbound_email, including the fields that are Base64 encoded.

Finally, you can step through the inbound email process with a debugger to see exactly how/why it’s failing to get the email. Start here: https://github.com/salesagility/SuiteCRM/blob/master/modules/Schedulers/_AddJobsHere.php#L560

(you want function pollMonitoredInboxesAOP, don’t confuse it with pollMonitoredInboxes which is the old version)

Thanks for pointing me in the right direction @pgr I’ll have a look at that file.

I’m using just a standard imap email from cpanel. It works no problem if I’m signed in as admin, it’s only when signed in as a user that doesn’t work. I’ve checked all the database tables and they appear to have the correct associations. This blog article on how the email folders work was super helpful in understanding it.

Angel's Blog: SugarCRM In-Depth: E-mail Folders

One other bug I’ve found while troubleshooting this is that if Admin creates the “personal” inbound or outbound email account for a user, it’s not available for the user either. This I solved because the “assigned user” of the email account ends up being admin and then the user doesn’t have access. By changing it to the user in the DB solves this issue, and this is what happens when the user creates their own email account. However, this is not the same thing going on with group emails. It’s super puzzling because it seems it’s making the correct entries in the DB for it to work. I’m suspecting it might be a security group or role issue that’s preventing the inboxes from showing. However, I can’t find a way to associate the inbound email account with a security group or role.

OK, some progress… at least a direction. If I’m logged in as the user and construct the url like this:

/index.php?module=Emails&action=index&folders_id=3ddc71c1-5488-bee6-06a1-645a6cf455ed

Where the id is the folder ID of the inbox, the user can see the inbox! So the issue (I think?) is with the email account switcher in the email module. For whatever reason, it doesn’t show the group email inbox as an option. However, the user clearly has access to it!

Ok need a little help here. I’m so close to fixing the group emails problem (I think!!!). I found exactly where the problem is, actually there are a few problems.

  1. in the folders table “is_group” is not getting populated. That’s a problem for another day, I just manually changed to “1” for now.

  2. There is an error in the code “isgroup” should be “is_group”. I fixed that. Easy. Now the real problem…

  3. In SugarFolders.php in the function retrieveFoldersForProcessing…

There is a filter I guess to filter out non group folders. In fact, if I change it up to not equal to one, I can see all folders as a user. I think we want the condition to be only IF, it’s the account created by the user OR if it’s a group account. However, the condition for “is_group” never returns true. My php skills to deal with arrays of arrays are not that great. Please help if you can!!! Here is the filter as well as the output (example) from the $return array:

FILTER:

$secureReturn = [];

        foreach ($return as $item) {
            if ($item->is_group === 1 || $item['created_by'] === $user->id || is_admin($user)) {
                $secureReturn[] = $item;
            }
        }

        return $secureReturn;

For non-admin users, this always returns false and no mailboxes, except the personal email is returned.

HERE IS THE $RETURN Array:

(
    [id] => 3ddc71c1-5488-bee6-06a1-645a6cf455ed
    [name] => INBOX (Reply)
    [has_child] => 1
    [is_group] => 1
    [is_dynamic] => 1
    [dynamic_query] => 
    [folder_type] => inbound
    [created_by] => 1
    [deleted] => 0
    [children] => Array
        (
            [0] => Array
                (
                    [id] => 4523a166-b4de-c2b4-41ed-645a6c90db8d
                    [name] => Drafts (INBOX.Sent)
                    [folder_type] => draft
                    [parent_folder] => 3ddc71c1-5488-bee6-06a1-645a6cf455ed
                    [has_child] => 0
                    [is_group] => 1
                    [is_dynamic] => 1
                    [dynamic_query] => 
                    [assign_to_id] => 
                    [created_by] => 1
                    [modified_by] => 1
                    [deleted] => 0
                )

            [1] => Array
                (
                    [id] => 4579d172-d34f-40ca-646f-645a6cf43e64
                    [name] => Sent Emails (INBOX.Sent)
                    [folder_type] => sent
                    [parent_folder] => 3ddc71c1-5488-bee6-06a1-645a6cf455ed
                    [has_child] => 0
                    [is_group] => 1
                    [is_dynamic] => 1
                    [dynamic_query] => 
                    [assign_to_id] => 
                    [created_by] => 1
                    [modified_by] => 1
                    [deleted] => 0
                )

            [2] => Array
                (
                    [id] => 459bb6bb-fb67-fb7b-4bc8-645a6cd4246f
                    [name] => Archived Emails
                    [folder_type] => archived
                    [parent_folder] => 3ddc71c1-5488-bee6-06a1-645a6cf455ed
                    [has_child] => 0
                    [is_group] => 1
                    [is_dynamic] => 1
                    [dynamic_query] => 
                    [assign_to_id] => 
                    [created_by] => 1
                    [modified_by] => 1
                    [deleted] => 0
                )

        )

)


So I want to show this one because group_id=1, the other two conditions seem to work of “is admin” and “created by” It’s just this one that’s not returning true on this line:

if ($item->is_group == 1 || $item[‘created_by’] === $user->id || is_admin($user)) {

I’ve tried a bunch of different things and just can’t get it to return true for “is_group” I’m sure it’s PHP 101 type thing, I just can’t get it.

I’ve tried this too and it doesn’t return true:

if ($item['is_group'] === 1 || $item['created_by'] === $user->id ) {

I think I got it!!!

 if ($item['is_group'] === '1' || $item['created_by'] === $user->id || is_admin($user)) {

This works and shows subscribed folders that are marked as “is_group” =1 Now I just have to tackle the problem of why the “is_group” is not getting populated when a group account is created.

I updated the bug here: Accessing Group Mailboxes Does Not Work · Issue #9028 · salesagility/SuiteCRM · GitHub

Great work Paul :tada:

The strict comparison === requires that the data types are exactly the same, not just “equivalent”. I am not sure, but perhaps it would be better, instead of giving it the string datatype with === '1' like you did, to just relax the comparison to == 1.

This would depend on the rest of the code that writes the value into the array.

It’s possible that a similar issue is breaking the code regarding the other issues you found.

Writing $item['is_group'] should be equivalent here to $item->is_group.

So, if this works, that’s how I’d suggest leaving it:

$item->is_group == 1.

1 Like

Thanks for that insight! I was going crazy why it didn’t return true. I never even thought to look at the equivalency. I tried your suggestion, it does not return true. However, I modified my change to be less restrictive as you suggest, and it works, and also the other issue I was having was after the change it was crashing out the folder selection in the user profile. That seems to work. I’ll do some more testing around this. Also my next problem is that when you create or update a group account, it doesn’t write “is_group” = 1 to the folders table. I’m going to work on that next.

Here is the modified line that works:

if ($item['is_group'] == 1 || $item['created_by'] === $user->id || is_admin($user)) {

1 Like

Ok for those following along in this thread… Whooo hoo! I’ve solved the second problem that group email folders don’t get marked with “is_group=1” when create or updated. So now with the below and my previous change, users should see group email folders .

Still issues:

  1. I noticed inbound email folders don’t get deleted when the inbound email account is deleted. I’ll have to figure that out.
  2. In the user settings, selecting the subscribed folders does not save correctly and gives user access to all group folders.

Anyway, here’s what I’ve done to fix the “is_group” problem.

public function save($addSubscriptions = true)
    {
		 $this->dynamic_query = $this->db->quote($this->dynamic_query);

// //////////////////////////////////////////////////////////////CHANGE		
// Retrieve the value of 'is_personal' for the current folder
 // Check if the current folder is a parent folder
if (!empty($this->parent_folder)) {
    $query2 = "SELECT is_personal FROM inbound_email WHERE id = " . $this->db->quoted($this->parent_folder);
} else {
    $query2 = "SELECT is_personal FROM inbound_email WHERE id = " . $this->db->quoted($this->id);
}

$r2 = $this->db->query($query2);
$is_personal = $this->db->fetchByAssoc($r2)['is_personal'];

// Set the value of 'is_group' based on the value of 'is_personal'
if ($is_personal == 1) {
    $is_group = 0;
} else {
    $is_group = 1;
}

////////////////////////////////////END  CHANGES
    

        if ((!empty($this->id) && $this->new_with_id == false) || (empty($this->id) && $this->new_with_id == true)) {
            if (empty($this->id) && $this->new_with_id == true) {
                $guid = create_guid();
                $this->id = $guid;
            }

            $query = "INSERT INTO folders (id, name, folder_type, parent_folder, has_child, is_group, " .
                 "is_dynamic, dynamic_query, assign_to_id, created_by, modified_by, deleted) VALUES (" .
                    $this->db->quoted($this->id) . ", " .
                    $this->db->quoted($this->name) . ", " .
                    $this->db->quoted($this->folder_type) . ", " .
                    $this->db->quoted($this->parent_folder) . ", " .
                    $this->db->quoted($this->has_child) . ", " .
					////////////////////////////////////////////////////////CHANGE
                //    $this->db->quoted($this->is_group) . ", " .//remove
				  $this->db->quoted($is_group) . ", " . // set the value of is_group here
				//////////////////////////////////////////////////////////////END CHANGE	
                    $this->db->quoted($this->is_dynamic) . ", " .
                    $this->db->quoted($this->dynamic_query) . ", " .
                    $this->db->quoted($this->assign_to_id) . ", " .
                    $this->db->quoted($this->currentUser->id) . ", " .
                    $this->db->quoted($this->currentUser->id) . ", 0)";

            if ($addSubscriptions) {
                // create default subscription
                $this->addSubscriptionsToGroupFolder();
            }

            // if parent_id is set, update parents has_child flag
            if (!empty($this->parent_folder)) {
                $query3 = "UPDATE folders SET has_child = 1 WHERE id = " . $this->db->quoted($this->parent_folder);
                $r3 = $this->db->query($query3);
            }
        } else {
            $query = "UPDATE folders SET " .
                "name = " . $this->db->quoted($this->name) . ", " .
                "parent_folder = " . $this->db->quoted($this->parent_folder) . ", " .
                "dynamic_query = " . $this->db->quoted($this->dynamic_query) . ", " .
                "assign_to_id = " . $this->db->quoted($this->assign_to_id) . ", " .
                "modified_by = " . $this->db->quoted($this->currentUser->id) . ", " .
				// CHANGE

				 "is_group = " . $is_group . " " .

				 // END CHANGE
                "WHERE id = " . $this->db->quoted($this->id);
        }

        return $this->db->query($query, true);
    }
2 Likes

Ok working on problem #3:When an inbound email account is deleted it doesn’t remove them from the folders table or the folders_subscription table and the users can see deleted inbound accounts in their listview (assuming 1 and 2 are fixed above) . There is a function to do this in SugarFolders.php but is never gets called.

I found that when an inbound email address get’s deleted, it runs \suitecrm\modules\InboundEmail/Delete.php

This is great! Now I know where to call the function delete() from SugarFolders.php.

HOWEVER, there is no relationship between the inbound email account and the Folders!!!

I don’t know how to pass the ID of the parent folder because there is no relation I can see to the inbound email account!

As an example Inbound email account ID = 9fd6f2b6-d27e-3a30-3860-646916d7b037
It has: Group Folder ID = 995b9d97-1d42-ef53-d1a6-64691630c162
and Group ID = 9959fa3c-0e7f-103d-5416-6469165df7eb (in inbound_email table)

Neither of which correspond to anything else in the database!

The ID of the parent folder (in folders table) that should be related to this is:
a6ae099b-15d2-f2bc-9a67-64691642f9bf

There’s no way to ID the folders to be deleted from the inbound email account. I guess I could look for the email address, but that’s not exact. Am I missing something does anyone know how I’d relate the Inbound email record in the DB to the folder record? (you’d think it would be by group folder ID but it’s not!). Maybe that’s another problem!!! When folders get created you’d think the group folder ID should reference the inbound email account ID.

In Inbound_email, did you look inside the BASE64 encoded field? I think it’s called stored_options

Thanks @pgr I was hoping you put me on the trail, but alas, no. There’s got to be a way!

Here’s what’s in the base64 encode.

a:13:{s:9:"from_name";s:6:"Reply4";s:9:"from_addr";s:30:"reply@igosalesandmarketing.com";s:13:"reply_to_name";s:6:"Reply4";s:13:"reply_to_addr";s:30:"reply@igosalesandmarketing.com";s:10:"only_since";b:0;s:13:"filter_domain";s:0:"";s:30:"email_num_autoreplies_24_hours";s:2:"10";s:26:"allow_outbound_group_usage";b:1;s:7:"mailbox";s:5:"INBOX";s:11:"trashFolder";s:11:"INBOX.Trash";s:10:"sentFolder";s:10:"INBOX.Sent";s:12:"isAutoImport";b:1;s:25:"leaveMessagesOnMailServer";i:1;}

I’m thinking to use the email address to match. I don’t really like that because it stores the email login (which in most cases is the email address) to match the folder. So I’d have to match on email address and is_group =1. I think that would pretty much limit it. I’d much rather match on an ID. I was thinking in a case for example where a user added sales@domain.com as a personal and the admin also setup a group account sales@domian.com. I guess though if I limit it to is_group=1 and deleted=0 then for the most part, I’m getting the right parent folder associated with the email account. It would be so much easier if there was a key to match on. It’s too bad the groupfolder_id isn’t the inbound email account ID.

Ok got it duh, the “inbox” folder ID = the inbound account id and then the sub folders, have a parent ID of the inbox folder!!! OK this works. I was looking at the top level folder. Not sure why this has a different ID, but anyway, I think I can use to solve.

1 Like

Paul, I think it’s great that you’re taking the time to really dive into this.

If you have any PHP questions that I can help with, feel free to ask. My time is well spent helping you because you help others, and because you’re clearly a good learner and making good progress as a developer. :muscle:

1 Like

Thanks @pgr, you’ve been a huge help over the years with SuiteCRM and given me just the right kind of feedback to keep me going in the right direction. While I’ve done a lot of front end web stuff with HTML and CSS, I’ve been really learning a lot of PHP over the last year or so mostly. Diving into these kinds of problems really helps me learn a ton, plus it’s the least I can do to give back to the community for a great CRM! I get most of the coding structures, (I learned them on a PET computer in BASIC 40 years ago if you can believe it!), I just don’t have the day to day experience with all the syntax of PHP yet, which is where I struggle.

1 Like

Still trying to get the deleted folders of a deleted email account marked as deleted. There seems to be functionality to do this in SugarFolders.php = public function delete()

Ok, I think I’m fundamentally misunderstanding something here.

When an inbound email account gets deleted. modules/InboundEmail/delete.php is called. Great.

I need to now run delete() public function in /include/SugarFolders/SugarFolders.php

I’ve already figured out how to get $inboundEmailID of the current account being deleted and the $current_user.

I’m trying to call the public function delete like this…

require_once('include/SugarFolders/SugarFolders.php');
$sugarFolders = new SugarFolder();

$sugarFolders->id = $inboundEmailId; // Set the ID of the folder to be deleted
$sugarFolders->currentUser = $current_user; // Set the current user object
$sugarFolders->delete();

I have an object when I echo out $sugarFolders, but the delete function is just not getting executed (I have a log there too).

Not sure, do I need to do something like $sugarFolders->retrieve($inboundEmailId); first (I tried that too and didn’t work)?

Ok, making progress! Never fails, as soon as I post a question another idea pops into my head.

Ok so trying to set the currentUser messed it up.

require_once('include/SugarFolders/SugarFolders.php');
$sugarFolder = new SugarFolder();

$sugarFolder->retrieve($inboundEmailId); // Get the folder to be deleted

$sugarFolder->delete();

}

Whoo hoo! delete is now being executed. Still not marking the folders deleted = “1” but that’ s tomorrow’s problem. Now I have to troubleshoot the delete() function and deleteChildrenCascade() function.

I would try (in the new SugarFolders command) calling the constructor with as many arguments as you have available.

Check those defaults there, no use in passing values if they are the same as those.

You can see there’s preparatory code there in the constructor, you should get a more functional object if you initialize it properly, not just add values to member properties.

Thanks @pgr problem #3 solved. I’ve successfully called the delete() function and it works as intended without any further modification. All related folders to the parent INBOX that is being deleted get marked deleted =1, including the parent INBOX folder. Ok on to problem #4.

This is what I added to Delete.php. I’m going to to a pull request. I’ve tested a couple of times. Just want to test with a user that is not admin and see what happens.

//delete related folders by calling delete() in SugarFolders.php
// Retrieve the ID of the inbound email account being deleted
$inboundEmailId = $focus->id;

// Create an instance of the SugarFolders class

require_once('include/SugarFolders/SugarFolders.php');
$sugarFolder = new SugarFolder();

$sugarFolder->retrieve($inboundEmailId); // Get the folder to be deleted

$sugarFolder->delete();

}