CiviCRM Core issueshttps://lab.civicrm.org/dev/core/-/issues2024-03-20T13:55:38Zhttps://lab.civicrm.org/dev/core/-/issues/5102Allow access to API params from Api4Query2024-03-20T13:55:38ZMichael McAndrewAllow access to API params from Api4QueryContext:
* I am adding an 'extra' calculated field to a spec provider for a custom entity.
* The value of this extra calculated field depends on a parameter passed to the get action.
* I have access to the query in the setSqlRenderer me...Context:
* I am adding an 'extra' calculated field to a spec provider for a custom entity.
* The value of this extra calculated field depends on a parameter passed to the get action.
* I have access to the query in the setSqlRenderer method (which contains a _protected_ API object) but there doesn't appear to be a way to access the API parameters.
Adding the following method to Api4Query allows read only access to API params and seems inline with other methods like `getSelect()`, but feel free to let me know if I am doing it wrong and there is a better approach.
```php
/**
* @return mixed
*/
public function getParam(string $param) {
return call_user_func([$this->api, 'get'.ucfirst($param)]);
}
```
PR coming up...https://lab.civicrm.org/dev/core/-/issues/5073Standalone default folder structure2024-03-08T10:24:03ZufundoStandalone default folder structureSome wise suggestions from @artfulrobot for clearer folder naming / out-of-the-box security:
> ```
> - civicrm-standalone-X.Y.Z.zip
> - index.php
> - .htaccess
> - robots.txt ➌
> - data/
> - data/ext/....Some wise suggestions from @artfulrobot for clearer folder naming / out-of-the-box security:
> ```
> - civicrm-standalone-X.Y.Z.zip
> - index.php
> - .htaccess
> - robots.txt ➌
> - data/
> - data/ext/.htaccess
> - data/public/.htaccess ➊
> - data/.private/.htaccess ➋
> - core/<ALL-THE-CODES>
> ```
>
> 1. use 'public' not 'upload'. It partners well with 'private', and 'upload' is such a daft relative-to-what? term (not everything you upload through a browser ought to be in a public dir). I think the whole 'persist' 'contribute' etc. is a right mess - or at least I don't understand the logic if there is logic, though last time I looked at it the logic was that at some point in history the first thing to allow uploads was civi contribute so ...
>
> 2. use a dot before _private/_. It's very common, and easy, to ban http access to all 'dot files'. So this gives an extra _likely_ shield against the "oh, I didn't realise nginx ignored .htaccess files" users. Just feels safer, if we're focussing on making this easy.
>
> 3. In terms of sensible defaults, I feel we should have a robots.txt that does its best to ban crawlers, especially AI ones, on everything except specific paths (e.g. event pages). It's one thing to have someone tell you you've accidentally exposed data and someone saw it, it's another to find that your [exposed data now lives in an LLM](https://arstechnica.com/information-technology/2022/09/artist-finds-private-medical-record-photos-in-popular-ai-training-data-set/) training set, ready to be given to anyone with a particular prompt.
Maybe to the nginx point, we could add `nginx-civicrm-site.conf.sample` in the root?
I think the principles for the public / private upload folder names apply to the composer template and the tarball.ufundoufundohttps://lab.civicrm.org/dev/core/-/issues/5068Do we need the qfkey in group urls?2024-03-07T13:49:27ZAndrew WestDo we need the qfkey in group urls?My users are forever emailing each other broken links to Civi groups, due to the qfkey in the URL. This doesn't seem to be needed in most of the rest of Civi - is it worth my investigating how to remove it? Or would we aim to replace tha...My users are forever emailing each other broken links to Civi groups, due to the qfkey in the URL. This doesn't seem to be needed in most of the rest of Civi - is it worth my investigating how to remove it? Or would we aim to replace that page with a search kit version soon anyway?https://lab.civicrm.org/dev/core/-/issues/5060Automated reminder to people that didn't subcribe to groups they applied subs...2024-03-14T22:45:20Znikola.mladenovicAutomated reminder to people that didn't subcribe to groups they applied subscription forOverview
----------------------------------------
Subscribing for newsletter usually ends up as a one way street, if they want to have subscribers and be under wing of GDPR. If the user doesn't confirm their subscription, they would eith...Overview
----------------------------------------
Subscribing for newsletter usually ends up as a one way street, if they want to have subscribers and be under wing of GDPR. If the user doesn't confirm their subscription, they would either have to find original email or resubscribe if they cant find it, but usually users just forget they did subscribe.
Recommended improvement was discussed on Mattermost to check if such feature existed in civicrm and few users did raise concern and that they would like to have this feature request:
[Mattermost chat](https://chat.civicrm.org/civicrm/pl/bg6x9u7qmf8fdkwn8tz4o9ft1r )
Current behaviour
----------------------------------------
Subscribing to newsletter only sends email as user applies for newsletter
Proposed behaviour
----------------------------------------
Scheduled job for example, which would check if there are any users which are pending for some groups, check the date they applied and then send another nudge for user to join in. This feature should be limited to only once per fresh request for all pending users. Proposal for after how much this automated schedueld job would contact users should be a week, 2 weeks, month, 2 months, 3 months.
Comments
----------------------------------------
_Anything else you would like the reviewer to note._https://lab.civicrm.org/dev/core/-/issues/5058FormBuilder "magic" links should log in the user so that User-based Entity se...2024-03-07T13:41:11ZKeith NunnFormBuilder "magic" links should log in the user so that User-based Entity security behaves as expectedOverview
----------------------------------------
When sending a link to a Form Builder form to a user by token (e.g. {afform.afformSubmissionLink}), the generated URL provides information that allows the form to auto-populate. It should...Overview
----------------------------------------
When sending a link to a Form Builder form to a user by token (e.g. {afform.afformSubmissionLink}), the generated URL provides information that allows the form to auto-populate. It should also log in the specific contact who received the link similarly to the behaviour of the contact_id and checksum tokens when used together.
Reproduction steps
----------------------------------------
1. create a new basic submission form
1. set 'Expose to:' to include 'Message Tokens'
1. set 'Individual 1' Security to 'User-based'
1. set 'Autofill' to 'current user'
1. Go to a contact summary page and merge document to use the exposed token.
1. Follow the link in the merged document
Current behaviour
----------------------------------------
Sometimes the form will throw an error, other times if the overall display permissions are open enough, the form will display and auto-populate, but any changes to the Individual 1 contact record will fail.
Expected behaviour
----------------------------------------
CiviCRM should recognize the user as a logged-in contact because we have sent them a 'magic' link.
Environment information
----------------------------------------
https://wpmaster.demo.civicrm.org test environment via firefox 123
Comments
----------------------------------------https://lab.civicrm.org/dev/core/-/issues/5055Does anyone use CRM_Mailing_Form_Approve?2024-03-07T13:37:04ZherbdoolDoes anyone use CRM_Mailing_Form_Approve?As part of clean up of accordions, we noticed that it's hard to test template for `civicrm/mailing/approve` which calls `CRM_Mailing_Form_Approve`. Is this being used? There's a permission as well which seems like it can't even be set. U...As part of clean up of accordions, we noticed that it's hard to test template for `civicrm/mailing/approve` which calls `CRM_Mailing_Form_Approve`. Is this being used? There's a permission as well which seems like it can't even be set. Unless I'm missing something.https://lab.civicrm.org/dev/core/-/issues/4979Should `die()` be called when a DB Query fails?2024-02-14T09:49:16ZhaystackShould `die()` be called when a DB Query fails?Overview
----------------------------------------
As the title says, when a DB Query fails, `CRM_Utils_File::runSqlQuery()` [calls](https://github.com/civicrm/civicrm-core/blob/fd4913060d8e944a99abe30c22ba670ed0e51a9a/CRM/Utils/File.php#...Overview
----------------------------------------
As the title says, when a DB Query fails, `CRM_Utils_File::runSqlQuery()` [calls](https://github.com/civicrm/civicrm-core/blob/fd4913060d8e944a99abe30c22ba670ed0e51a9a/CRM/Utils/File.php#L327) `die()` and all script execution stop *ahem* dead. Is this intentional?
Reproduction steps
----------------------------------------
1. Install the "Deduper" Extension (v1.7) via the API (sorry @eileen :dove:)
1. See the unrecoverable error "**Cannot execute INSERT INTO civicrm_contact_name_pair_family (id, name_a, name_b, is_most_common_form, is_active) VALUES (65, 'Takahashi', '髙𣘺', 0, 1)**".
Current behaviour
----------------------------------------
There's no way to trap this and continue with a script that installs Extensions via the API.
Edit: apparently there [is a way](https://stackoverflow.com/a/16925225) but this seems a less than sensible way to go about trapping the error.
Expected behaviour
----------------------------------------
A script that installs Extensions via the API should be able to trap the error as an `Exception` with `try/catch` and act accordingly.https://lab.civicrm.org/dev/core/-/issues/4947Proposal: Store Contribution Page ID in the `civicrm_contribution_recur` table2024-02-01T10:23:33ZjaapjansmaProposal: Store Contribution Page ID in the `civicrm_contribution_recur` tableOverview
----------------------------------------
When a recurring contribution is created with a contribution page. Also store the contribution page ID in the `civicrm_contribution_recur` table.
This works already for 'normal' contrib...Overview
----------------------------------------
When a recurring contribution is created with a contribution page. Also store the contribution page ID in the `civicrm_contribution_recur` table.
This works already for 'normal' contribution.
Example use-case
----------------------------------------
1. CiviRules can be used when a new Contribution Recur is added and a condition can then be used which Contribution Page this came from.
Current behaviour
----------------------------------------
Currently for the 'normal' contributions the contribution page ID is stored in the `civicrm_contribution` table.
Proposed behaviour
----------------------------------------
1. Add a column to the `civicrm_contribution_recur` table called `contribution_page_id`
2. When a contribution recur is added through a Contribution Page then store the contribution page id into this column
3. Optional we can show the data from this field in the user interface when a user views a contribution recur
Workaround
----------------------------------------
There is a workaround with an extension: https://lab.civicrm.org/extensions/storecontributionpageidoncontribrecurjaapjansmajaapjansmahttps://lab.civicrm.org/dev/core/-/issues/4915Sending an invoice per email goes to primary address, not to billing2024-02-09T23:08:39ZMariaVSending an invoice per email goes to primary address, not to billingI have found this post on StackExchange:
https://civicrm.stackexchange.com/questions/9094/sending-an-invoice-per-email-goes-to-primary-address-not-to-billing
I tested on CiviCRM 5.68.1 and it seems that CiviCRM still only sends the invo...I have found this post on StackExchange:
https://civicrm.stackexchange.com/questions/9094/sending-an-invoice-per-email-goes-to-primary-address-not-to-billing
I tested on CiviCRM 5.68.1 and it seems that CiviCRM still only sends the invoice to the primary address.
What do you think about a setting 'send invoice email to Billing if it exists, otherwise send it to Primary'?
The setting could be optional so that nothing changes for users who want to keep it as it is.
Or is there any knowing extension already that I could not find?
Thanks in advance!https://lab.civicrm.org/dev/core/-/issues/4908Integrate Angular calendar for better usability2024-01-15T11:36:56ZshaneonabikeIntegrate Angular calendar for better usabilityI don't mind the existing calendar date picker, but I was wondering if there are other solutions that might work a bit better for usability. I came across this [Angular version](https://laros.io/using-angular-material-calendar-with-date-...I don't mind the existing calendar date picker, but I was wondering if there are other solutions that might work a bit better for usability. I came across this [Angular version](https://laros.io/using-angular-material-calendar-with-date-ranges-and-range-presets) (maybe there is an alternative)
Since we are already starting to use a lot of Angular I thought it could be something to consider. There is even a date range option, which could be interesting for other areas.
You can spin a demo on this stackblitz https://stackblitz.com/edit/angular-material-calendar-with-date-ranges-and-presets?file=package.json
What I like about it:
* When choosing a different year it automatically prompts to choose the month which is fairly logical and usable
* There is the possibility to set ranges (future integration with Events?)
* There is the option to have quick options like Last 30 days, Last 12 months, etc.
![Angular Material Calendar with Date Range and Range Presets](https://laros.io/images/angular-material-calendar-date-range.png)
Anyway it was just something I wanted to raise but it's not a high priority but could be a great enhancement.https://lab.civicrm.org/dev/core/-/issues/4744Proposal - remove 'record contribution' from back office participant form whe...2023-11-02T15:27:49ZeileenProposal - remove 'record contribution' from back office participant form where pending contribution existsIf a participant record is updated where a pending record exists you need to update the status from pending to completed
![image](/uploads/1450500ef919017c7eee5c9ef326de6c/image.png)
![image](/uploads/87bc885b56fd756e897dc416a47eccd9/...If a participant record is updated where a pending record exists you need to update the status from pending to completed
![image](/uploads/1450500ef919017c7eee5c9ef326de6c/image.png)
![image](/uploads/87bc885b56fd756e897dc416a47eccd9/image.png)
However - this ONLY updates the contribution - without a separate action to update the participant status the participant status is unchanged
This is wildly confusing.
I propose we don't show 'record contribution' section when there is an existing payment & instead offer an 'add payment' button that pops up the Add payment form if clicked...https://lab.civicrm.org/dev/core/-/issues/4620CiviEvent dashboard: start date is 7 days ago2023-09-26T12:23:49ZmasettoCiviEvent dashboard: start date is 7 days agoI follow up https://civicrm.stackexchange.com/questions/22424/how-to-customize-events-dashboard.
The event dashboard now shows recent and upcoming events (where start date is 7 days ago OR later).
It would be convenient if the numbe...I follow up https://civicrm.stackexchange.com/questions/22424/how-to-customize-events-dashboard.
The event dashboard now shows recent and upcoming events (where start date is 7 days ago OR later).
It would be convenient if the number of days was configurable in the preferences of CiviEvents (`civicrm/admin/setting/preferences/event`).
Do you think it is useful and required by others as well?https://lab.civicrm.org/dev/core/-/issues/4564Idea: Smart groups without the cache2023-09-13T12:20:47ZAndrew WestIdea: Smart groups without the cacheJust throwing this out there as something we could maybe fund:
Current behaviour
----------------------------------------
Smart groups always write to the civicrm_group_contact_cache table
Proposed behaviour
---------------------------...Just throwing this out there as something we could maybe fund:
Current behaviour
----------------------------------------
Smart groups always write to the civicrm_group_contact_cache table
Proposed behaviour
----------------------------------------
Smart groups optionally write to the civicrm_group_contact_cache table (default: yes)
Overview
----------------------------------------
As I understand it, whenever you request a smart group it stores its contacts in civicrm_group_contact_cache. This is for good reason: it's a cache that means the smart groups aren't regenerated every time they're requested, which can be slow.
It also simplifies the code: you can effectively treat smart groups the same as non-smart groups - as it's just grabbing a list of contacts from a table.
But it's possible to set the cache to expire after 0 seconds, if you need your smart groups to be up to date: ie. they are regenerated each time they're requested. I think a fair few orgs do this.
If you have a 0s cache, you sometimes don't need the data written to a table at all. You just need a transient list of contacts that doesn't need to persist past the end of the request. And it's pretty slow to use the cache table: the 'INSERT INTO' command is slow relative to the original SELECT query. This time can add up if you're querying a bunch of groups at once, or have nested smart groups, or have a lot of contacts in the groups.
So I was tentatively wondering if there could be an API4 'no-smart-group-cache' parameter, that just runs the underlying query and stores it all in memory. I imagine this would only work for API4 smart groups, as it's super-easy to get their underlying search criteria.
The default would remain to use the cache, for full backwards-compatibility. But the parameter could be used in custom code.
Two questions:
1. Anyone else interested in this?
2. How hard would it be / how much of a can of worms is it?https://lab.civicrm.org/dev/core/-/issues/4558Proposal: setting to define alternative frontend URLs for when using a proxy2023-09-13T12:12:51ZbgmProposal: setting to define alternative frontend URLs for when using a proxyWhen CiviCRM runs behind another system, such as a reverse-proxy running on a separate domain that only exposes specific features, we do not have a way to define that URL.
For example:
- Staff access crm.example.org - the service is ru...When CiviCRM runs behind another system, such as a reverse-proxy running on a separate domain that only exposes specific features, we do not have a way to define that URL.
For example:
- Staff access crm.example.org - the service is running behind a VPN and not available to the general public
- However, they send mailings, which include unsubscribe and opt-out links. This feature is provided by a reverse-proxy that forwards those requests specifically.
[CRM/Mailing/BAO/Mailing.php](https://github.com/civicrm/civicrm-core/blob/master/CRM/Mailing/BAO/Mailing.php#L1020) defines a few URLs and uses `CRM_Utils_System::url` to generate the links. In the example I found, they were overriding the core PHP file, in order to hardcode the domain.
~~I'm thinking mailing [hook_civicrm_links](https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_links/) might be a good semantic fit, even if it's a bit of an odd case, but it is used is weird places, such as in the Extension Manager.~~
- `op` = `mailing.content.actions` ? (mailing.actions could be confusing with actions in the UI, such as edit/delete)
- `objectID` = `job_id` ?
~~Where it gets weirder, is that the array of `$urls` would have to be packaged into the format that the hook expects, then unpackaged back into the expected format from the parent function (unless we just send it as-is, because the fluff is not necessary, and it's up to the hook to check for the `$op`).~~
An alternative could be to add a special `CIVICRM_UF_BASEURL`, but then it would mean more params for `CRM_Utils_System::url` and it seems already messy enough.
Edit: based on Jaap's comment, it probably makes more sense to find a solution for a "reverse-proxy URL".https://lab.civicrm.org/dev/core/-/issues/4557Proposal - Allow empty profile field labels instead of null2023-09-08T15:11:53ZshaneonabikeProposal - Allow empty profile field labels instead of nullPresently, when you create a Custom Profile and add fields you need add a label. In some cases, having a large question makes the label kind of awkward and crammed. It makes for a bad UX experience I believe.
So the workaround by most o...Presently, when you create a Custom Profile and add fields you need add a label. In some cases, having a large question makes the label kind of awkward and crammed. It makes for a bad UX experience I believe.
So the workaround by most of the designers I work with is to simply remove the ```label``` and then drop the question in the Pre text. This works just dandy, except that CiviCRM is adding ```null``` for empty texts. The only way to prevent this is to modify the label in the DB.
I would like to propose that we remove the code that is setting this text to null. I could make a quick patch. I realize the label is being used to create the actual field name, but in the case of the Profile the field name is already created.
![Selection_007](/uploads/48ab9decd2a6b1c3bb5a380a8278f12d/Selection_007.png)https://lab.civicrm.org/dev/core/-/issues/4544Older sites missing index for `is_deceased` on `civicrm_contact`2023-09-06T13:56:12ZwmortadaOlder sites missing index for `is_deceased` on `civicrm_contact`This is a follow up from Eileen's comment about a missing index in https://lab.civicrm.org/dev/core/-/issues/4539#note_150479.
The index for `is_deceased` was added in CiviCRM 4.7 and is defined in [xml/schema/Contact/Contact.xml](https...This is a follow up from Eileen's comment about a missing index in https://lab.civicrm.org/dev/core/-/issues/4539#note_150479.
The index for `is_deceased` was added in CiviCRM 4.7 and is defined in [xml/schema/Contact/Contact.xml](https://github.com/civicrm/civicrm-core/blob/f068222ac25a5a393e9238b617ae91a4d9d29797/xml/schema/Contact/Contact.xml#L722-L726). However, there doesn't appear to be an upgrade task to add this index to existing sites. As a result any site installed before CiviCRM 4.7 is likely to be missing this index. Checking our client sites, I can see a mix of sites that do and don't have it.
The MR where this index was added is: https://github.com/civicrm/civicrm-core/pull/10489
There was a question from Tim about whether there should be an upgrader. There was a later comment from Eileen about whether this index should be removed.
So I guess the question is whether we want this index or not:
- if we **do** want this index we should add an upgrade task so that older sites have this index
- if we **don't** want this index we should add an upgrade task to remove it from sites that have ithttps://lab.civicrm.org/dev/core/-/issues/4528Entity Reference custom fields: Configurable delete behavior2024-01-17T22:28:23ZjensschuppeEntity Reference custom fields: Configurable delete behavior## Overview
Currently, when entities referenced in a custom field of the type _Entity Reference_ are deleted, the entity reference field gets cleared (set to `NULL`). This is easy and prevents us from database inconsistencies. But this ...## Overview
Currently, when entities referenced in a custom field of the type _Entity Reference_ are deleted, the entity reference field gets cleared (set to `NULL`). This is easy and prevents us from database inconsistencies. But this can lead to problems:
* inconsistencies when the entity reference field is set to be required
* stale/orphaned entities the entity reference field is attached to
* `civicrm_value_*` records with all-empty columns (or are they being taken care of already)
## Example use-case
Imagine a custom entity named _Qualification_ with (among others) an entity reference custom field referencing a _Contact_ entity, which is set to be required; read: A _Contact_ has multiple _Qualifications_.
## Current behaviour
When deleting the contact, the _Qualification_ entity will not be deleted until done so manually. The reference is just being set to `NULL` instead. Those entities will be meaningless without a referenced contact, the required entity reference custom field will be empty, which breaks the semantic contract.
Question: Is the `SET NULL` behavior a FK constraint on the DB table or part of some logic in Core code?
## Proposed behaviour
When deleting the contact, the _Qualification_ entity should be deleted. This is what we'd have configured the entity reference field to behave, as in other scenarios, the current behavior will be totally fine.
So, we'll need a configuration option for _Entity Reference_ custom fields for selecting what should happen upon deletion of the referenced entity. Available options could be (basically MySQL foreign key constraints):
* Remove Reference (`SET NULL`) - current behavior, maybe also the default option
* Cascade Delete (`CASCADE`) - removes the _`civicrm_value_*`_ record and the entity the custom group is attached to
* Restrict Delete (`RESTRICT`) - just as you can't e. g. delete contacts with financial transactions
* Set Default (`SET DEFAULT`) - to be configured on the custom field
* ~~Keep Reference (`NO ACTION`)~~ - might not be a good idea
## Comments
@colemanw and @michaelmcandrew might be interested.
Currently, this can be partly achieved by implementing a `preDelete` event for retrieving and deleting referencing entities.https://lab.civicrm.org/dev/core/-/issues/4511Allow multiple 'Added By' for an activity2023-09-08T20:35:49ZyashodhaAllow multiple 'Added By' for an activityThe way some clients are using the CRM, the 'Added By' person(s) is/are really the person(s) responsible for making the activity happen, and the 'Assigned To' are people that participate in the activity but are not directly responsible f...The way some clients are using the CRM, the 'Added By' person(s) is/are really the person(s) responsible for making the activity happen, and the 'Assigned To' are people that participate in the activity but are not directly responsible for it.
The issues is that the CRM only allows ONE Added By, so they rely on some custom contact reference fields 'Partnered With' to supplement the Added By. But this is not a good solution and has many limitations e,g not being able to see the activity in Partner tab etc.
Therefore the best way to move forward is to allow multiple 'Added By' for an activity. The database schema already allows for that with the _activity_contact_ table.
From technical perspective, I reckon it was an oversight to not allow multiple contacts for Contact Source as we had moved civicrm_activity.source_contact_id (single), civicrm_activity_target.contact_id, civicrm_activity_assignee.contact_id (dropped civicrm_activity_target/civicrm_activity_assignee tables) in favor of civicrm_activity_contact table.
Screens that need to be modified:
- Activity tab in the Contact screen
- Activity View screen
- Activity Edit screen
- Activities reports
- Activities resultyashodhayashodhahttps://lab.civicrm.org/dev/core/-/issues/4508CiviMail Auto complete includes unsent mailings2023-08-24T13:43:34ZseamusleeCiviMail Auto complete includes unsent mailingsAs reported by Jon Goldberg it seems the current CiviMail Recipients Auto complete includes unsent mailings https://github.com/civicrm/civicrm-core/pull/27071#issuecomment-1682481080As reported by Jon Goldberg it seems the current CiviMail Recipients Auto complete includes unsent mailings https://github.com/civicrm/civicrm-core/pull/27071#issuecomment-1682481080https://lab.civicrm.org/dev/core/-/issues/4495Proposal pseudoconstant facade2023-08-17T14:18:27ZeileenProposal pseudoconstant facadeI have long found using the core pseudoconstant functions a bit error prone as I 'guess' the BAO names & mix 'Contribute' & Contribution etc
Perhaps we should add
```
\Civi::pseudoConstant($entity)->getKey($name, $fieldName);
\Civi::ps...I have long found using the core pseudoconstant functions a bit error prone as I 'guess' the BAO names & mix 'Contribute' & Contribution etc
Perhaps we should add
```
\Civi::pseudoConstant($entity)->getKey($name, $fieldName);
\Civi::pseudoConstant($entity)->getLabel($id, $fieldName);
\Civi::pseudoConstant($entity)->getName($id, $fieldName);
(e.g value could be 'icon')
\Civi::pseudoConstant($entity)->getValue($id, $fieldName, $value);
```
I have some misgivings about the work pseudoConstant - it could be '\Civi::options()' or something else
Importantly `$entity` is the short-name
- I just added a note to docs discouraging people from randomly using core functions & suggesting they use only ones that are explicitly for external use & realised that there is no explicitly supported version of these super important functions