New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Msg template rendered content api #17162
Conversation
(Standard links)
|
6420d73
to
80831a5
Compare
This will need some thought to get this right. I'm not sure that |
@mattwire "This will need some thought to get this right" - agree. When it comes to api calls we lock in the names & contract so we want to like them. I'm not sure the distinctions of rendered / non-rendered? It parses each line for tokens - I feel like we would want to separate out 'just parsing tokens' & also parsing 'smarty tokens' - I have a feeling that at this stage you could pass a param & it might parse smarty - which needs to be managed from a security point of view. At this stage it's fair to say the api 'parses content, replacing non-smarty-tokens'. My vision was that it would also retrieve it based on the message_template |
Hey @eileenmcnaughton so:
You may want the contents of a message template pre- or post-token replacement / smarty processing. So "raw" (as saved in DB) or rendered. An example of this is extensions such as EmailAPI/PDFAPI which require a raw copy of the messagetemplate and then do the rendering themselves. Then the content: The messagetemplate table has three renderable fields - I like the idea of passing in strings instead of templates but I don't like the two options - html/text as it unnecessarily constrains things and I'm not sure of the need for having two. I imagine in the future you might want to pass other things in - a richtext string or a twig template that renders to a mobile app. I do think that there should be two separate API functions for getting the unrendered copy of a template and for rendering (replacing tokens / parsing templating language). |
@mattwire - I think for non-rendered you would just use MessageTemplate::get |
OK so more detail
|
@colemanw have you got thoughts on how this should look? |
80831a5
to
4bf65ef
Compare
I've updated this to use setAdHocStringToParse rather than specified strings. |
test this please |
53bfdca
to
a1efae7
Compare
Actually jenkins couldn't cope with them unmerged.. |
Rename action to 'render', setEntities to setEntity Also setAdHocString is renamed to addMessage
a1efae7
to
f82f930
Compare
* | ||
* @var array | ||
*/ | ||
protected $entityIds = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If $entity
is single-valued then this should probably be as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No - one entity - ie Activity - but many ids - results in a row per id returned - that is how it's used in the PdfTemplate code - which might be printing multiple pdfs for multiple activities
@eileenmcnaughton +1 on naming Regarding passing entityIDs:
We should do our best to align naming this with the schema param in tokenProcessor, or align the schema param with this. I like the above - not sure if that means we should tweak tokenProcessor again. Thinking along the lines of the gsoc project related to messagetemplates as well as some work I'm doing around TWIG for templates and this veda-consulting-company/uk.co.vedaconsulting.mosaico#361 ... Perhaps we don't add support initially for parsing smarty etc. but I think it should be part of this API later. That would work well with adding a |
@mattwire yeah - re twig / smarty. I'm on the fence as to whether it would be part of this api or another action - ie pass the results to MessageTemplate::renderSmarty or similar |
@mattwire also on the TokenProcessor - I find 'schema' a bit confusing - so yeah - maybe changing to above is good - we can still tinker on that. I was hoping to get this merged & then try something on a contribution template & see what that threw up |
I'm a bit wary about What about: $result = \Civi\Api4\MessageTemplate::render()
->addMessage(['string' => $html_message, 'format' => 'text/html', 'key' => 'html_string'])
->setContext(['activity_id' => 123])
->setCheckPermissions(FALSE)
->execute(); Then pass that through to the TokenProcessor (perhaps modulo some whitelist/validation/permission checks). |
Hmm - I don't have a super strong opinion on that. I find context a bit 'vague' but I suppose that's the point. But it feels a bit painful to use. If we start with an array of entity IDs it seems like you would quickly wind up writing a wrapper in the calling code o convert [1,6,7,9] to
At which point you have to wonder why the helper wouldn't be on the api itself.... |
@eileenmcnaughton Picking up on what @totten said re context. We need an unambiguous way to pass one or more IDs for different entities for each returned (rendered) template. For example this kind of thing has always been hard in CiviCRM but is possible using tokenProcessor and emailapi. It is particularly useful when sending notification/informative emails about progress in a case when there is a relationship between case client and coordinator:
That's how tokenProcessor works - you set the "schema" and context for the tokens which can be mapped to a set of entities. I'm not sure how that could work with:
but it does work (for a single row) with:
I can see two ways of doing this:
|
Right so we have our common scenario where you have a base entity - which is commonly used ie - I want to send a template-based-email to these 50 contributions - and we want to be able to do that simply - so we set the base entity & the ids
We also have a situation where we have a membership with a specific contribution. So at that point we need to pass something more like
That might not be exactly the look - but we want to have a fairly easy way to do it for the normal case & a more nuanced way for the less common complex case. I guess that latter one could be called 'setContext' - I find that kinda vague & unclear but that's not a show stopper. I don't see any of the patterns (or the underlying code or templates) having a way to deal with multiple activities and at some point we are getting past the scope of what this api should do |
@eileenmcnaughton Something like that would work well I think - we should mimic tokenProcessor where possible if it makes sense eg. https://lab.civicrm.org/mattwire/emailapi/-/blob/newtokenprocessor/api/v3/Email/Send.php#L275
TokenProcessor already supports this way of working but not all entities support tokenProcessor yet. It doesn't need to be perfect as we can extend it but let's get it as close as we can. I've done a few live sites for clients with the tokenProcessor and experimental emailapi and I can tell you it's really nice to be able to pass in a bunch of entities and know that all the tokens will render properly! Even in the first case where it's just a bunch of contributions - they're still linked to a contact ID so you either rely on the contribution being used to map back to the contact so that contact tokens (eg. name) can be rendered or you pass in a contact ID. |
@mattwire - OK - I'm kinda losing it on this one then. I think we want this api to easily support setEntityIDs for the main entity and it should accept an array of ids. I'm open to extending with other methods but I've kind of lost sight of how to resolve this now. I don't want to make it not do what I think it needs to do (accept an array of the main entity ids) in a simple way that doesn't require the calling code to iterate through that array first. I'm not sure if you are a) proposing to change this so it no longer does that or |
@eileenmcnaughton I think we're nearly there.
This feels too limiting to me as we are restricting to a single entity type and are we assuming that it's one entity per template? It would be easier if triggering the API manually but I assume it'll normally be scripted / automatic in which case a bit more verbosity would not matter? Do we want to call this API to render multiple templates at once, or should we call it once per set of entities? To render multiple something like:
and
The old token stuff doesn't support multiple entities but the new tokenProcessor should be able to and it would be nice to have the ability to send out summaries like the above - eg. "you've made the following contributions for this membership." We could implement for now just taking the first ID passed for each element (but supporting them coming in as an array or integer). |
I just don't see how
works. But I'm fine it being a second round thing or another api. I still don't think the fact the token processor could support something more complex means we shouldn't have an easy way to do the thing we know we want it to do |
I had a talk with @totten today & there is not a clear path forward on this so I'm closing it. Some key points that came out of our discussion
|
As an aside this is an example of the sort of thing I'd been thinking to cleanup with a render api
You can see that it sends emails from this function - unless the function is really only being called to render the content - in which case it skips that bit.... |
Overview
This PR adds a new proposed apiv4 api:
MessageTemplate::render
Before
After
Api created...
Technical Details
In conjunction with looking at #16983 and also the general mess around message templates (and some thoughts I have about using them), I think we should look at an api for them. I think the
render
needs to be one action but we should probably have other actions likesend
andpdf
oroutput
- where pdf is an option at a later point.Note that for retrieving un-rendered fieldMessageTemplate::get remains the goto- this new api for getting content parsed for CiviCRM (but not currently Smarty) tokens. It should parse one or more ad hoc strings or, if message_template_id is passed in, retrieve and parse the message template strings (this is not yet implemented).
When I added Contribution.sendConfirmation a long long time ago I feel like it was suggested there could be some security concerns - so this might need a bit of tightening so it doesn't return fields that should not otherwise be visible. At the moment Smarty is just not parsed because we would need to actively set context to smarty for that to work (and that needs more thought).
We will need to agree how this should look....
I chose the MessageTemplate entity as it made sense to me that we would want to be able to passing in the message_template identifier rather than a string, in which case we would also return the fields for the message template (msg_html, msg_text, msg_subject)
Also - it has the functions
setEntityIDs
andsetEntity
- note that in future we might want dependent entities - e.g the main entity is membership but there might be a contribution & we might use setEntities along with setRelatedEntityIds to handle thatNote this includes #17161
Comments
Also note I've updated existing tested code to call this api to provide the first round of test cover
@colemanw @totten @mattwire @demerit @Adyun @seamuselee001