Mass SMS with many Contacts causes slow loading of Manage Case screen for ACL users
Overview
The combination of Mass SMS Activities with many recipients, and ACL access to Contacts for Users who do not have "view/edit all contacts", causes very long loading times for Manage Case screens.
Mass SMS Activities apply to Contacts, not Cases. Due to the way the $params
are converted to $activityParams
, the $case_id
is never passed to the final API call. This means that non-Case Activities go through permission checks when loading the Manage Case screen. These ACL checks can take quite a long time (e.g. 30 Mass SMS Activities with 2k recipients each means 60k ACL permission checks).
Bulk Email Activities are handled differently (possibly to avoid this type of issue). Changing the Activity Type from Mass SMS to Bulk Email with an SQL query provides a workaround, but not a great solution.
Reproduction steps
- Set up ACL access to Contacts, so a User does not have "view all contacts" or "edit all contacts", but has ACL access to view and edit (at least some) Contacts
- Enable CiviCase
- Create a Case for a Contact who can be accessed by the ACL user
- Create a few (dozen) Mass SMS Activities with thousands of Contacts including the Contact from step 3
- Log in as the ACL user from step 1
- Load the Manage Case screen for the Case from step 3
- Observe that it takes longer than normal
- Set is_deleted=1 or change the Activity Type to Bulk Email for all Activities created in step 4
- Load the Manage Case screen again, and observe that it's much faster
Current behaviour
Most of the time is spent inside the controller->run()
call here:
https://lab.civicrm.org/dev/core/-/blob/5.57.0/CRM/Case/Page/Tab.php#L98-112
That whole block can actually be removed, and superficially the page appears to work (without the speed issues), though I haven't tested much. Just noting that it doesn't have any immediately obvious effect on the page.
Looking into why it takes so long, I think it relates to the parameters prepared by this getActivites call:
https://lab.civicrm.org/dev/core/-/blob/5.57.0/CRM/Activity/BAO/Activity.php#L579-583
At that point, $params["caseId"]
contains an integer. However, it doesn't make it into $activityParams
. If you observe what happens here, I think $activityParams["case_id"]
will always contain ['IS NULL' => 1]
.
https://lab.civicrm.org/dev/core/-/blob/5.57.0/CRM/Activity/BAO/Activity.php#L2287-2291
Since self::activityComponents()
is called without arguments, it will never contain CiviCase:
https://lab.civicrm.org/dev/core/-/blob/5.57.0/CRM/Activity/BAO/Activity.php#L851-861
The comment "In practice this means should we include CiviCase in the results" seems to imply the reverse, but stepping through activityComponents()
in the debugger, I see:
$compInfo["CiviCase"]->info["showActivitiesInCore"] = (bool) 0
Even if CiviCase were included in the results though, the case ID would not make its way from $params
into $activityParams
:
https://lab.civicrm.org/dev/core/-/blob/5.57.0/CRM/Activity/BAO/Activity.php#L2289-2291
The end result of all of this is that the controller is checking permissions for every Activity relating to this contact, not just the ones which will be displayed on the Case. When each Activity has thousands of Contacts (as with Mass SMS), these checks become very slow.
Also, the Activities section of the Manage Case screen seems to be populated by a subsequent process. Removing this controller has no obvious effect (except reducing the loading time), raising the question of whether it's needed at all.
Expected behaviour
Mass SMS Activities with many contacts should not slow down the loading of Manage Case screens (noting that a Mass SMS cannot be applied to a Case, only to a Contact).
Environment information
- Browser: Chromium Version 108.0.5359.124 (Official Build) Arch Linux (64-bit)
- CiviCRM: 5.57.0
- PHP: 7.4
- CMS: Drupal 7.92
- Database: mysql Ver 15.1 Distrib 10.5.18-MariaDB
- Web Server: Apache 2.4