Skip to content
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

dev/core#71 Add functionality to create PDF/Word docs from Activity searches #12012

Closed
wants to merge 2 commits into from

Conversation

aydun
Copy link
Contributor

@aydun aydun commented Apr 21, 2018

Overview

Add a Print/Merge Document action to Activity searches

Before

Currently most other searches provide a Print/Merge action - except for Activities.

After

This adds a Print/Merge action to Activity search results

Technical Details

Activity tokens are defined but not evaluated in the legacy token system. They are functional in the new token system so this uses the new Civi\Token\TokenProcessor

This revises or updates classes to use the following class hierarchy:

  • base class: CRM_Core_Form_Task_PDFLetterCommon
    • defined functions: renderFromRows($rows, $msgPart, $formValues)
    • subclass: CRM_Contact_Form_Task_PDFLetterCommon
      • defined functions: getLoggingOptions(), preProcess(&$form), preProcessSingle(&$form, $cid), buildQuickForm(&$form), setDefaultValues(), formRule(...), processMessageTemplate($formValues), postProcess(&$form), createActivities(...), formatMessage(...), getMimeType(), getTokenCategories()
      • consumed functions (from parent): none
      • subclass: CRM_Activity_Form_Task_PDFLetterCommon
        • defined functions: postProcess(&$form), postProcessActivities(&$form, $activityIds), createTokenProcessor(), listTokens()
        • consumed functions (from parent): processMessageTemplate(), renderFromRows()
  • base class: HTML_Common => HTML_QuickForm => HTML_QuickForm_Page => CRM_Core_Form
    • subclass: CRM_Activity_Form_Task
      • defined functions: preProcess(), preProcessCommon(&$form), setContactIDs(), addDefaultButtons(...)
      • subclass: CRM_Activity_Form_Task_PDF
        • defined functions: preProcess(), setDefaultValues(), buildQuickForm(), postProcess(), listTokens()
        • consumed functions (from parent): preProcess()

Comments

Need to add some tests, but looking for feedback on the use of TokenProcessor first

@aydun
Copy link
Contributor Author

aydun commented Apr 21, 2018

@totten - Would you mind taking at look at this as it uses the new token processor?

FYI @guanhuan @mattwire @monishdeb re https://chat.civicrm.org/civicrm/pl/7ih15ybjd7yzm8184yc4b1jmxo

@mattwire
Copy link
Contributor

@aydun Is there any work that could be done here to create abstract core tasks for PDF / PDFLetterCommon as I did when adding the new "Batch Update" functionality for cases in #11411 as that would help in the future to improve maintainability and possibly offer a route to migrating all PDF tasks across to the new token processor at some point in the future.

return $html;
}

if (!empty($html)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this section could be shared with another class rather than duplication? - perhaps from getRows down could be renderHtmlFromRows or similar?

@aydun
Copy link
Contributor Author

aydun commented Apr 26, 2018

@eileenmcnaughton , @mattwire I've moved the rendering part to a new function in a new base class. CRM_Contact_Form_Task_PDFLetterCommon has functioned as a base class for all the *_PDFLetterCommon so I've changed that to use the new class. We can move truly common stuff to the new class and leave the Contact one with contact-specific code.

@monishdeb
Copy link
Member

monishdeb commented Apr 28, 2018

Here's few suggestions after code review:

  1. End child class CRM_Activity_Form_Task_PDF extends parent CRM_Activity_Form_Task. That's fine but in form function we are using CRM_Activity_Form_Task_PDFLetterCommon::[preProcess|buildQuickform|postProcess] because it inherits the function from CRM_Contact_Form_Task_PDFLetterCommon so why can't we directly use its function rather then having an intermediate class? Such as
diff --git a/CRM/Activity/Form/Task/PDF.php b/CRM/Activity/Form/Task/PDF.php
new file mode 100644
index 00000000000..f1c4c5dd68a
--- /dev/null
+++ b/CRM/Activity/Form/Task/PDF.php
@@ -0,0 +1,79 @@
...
public function buildQuickForm() {
-     CRM_Activity_Form_Task_PDFLetterCommon::buildQuickForm($this);
+     CRM_Contact_Form_Task_PDFLetterCommon::buildQuickForm($this);
}

And it will no longer need us CRM_Activity_Form_Task_PDFLetterCommon which is just a shell class to extend CRM_Contact_Form_Task_PDFLetterCommon

  1. Then move the all utility functions to respective Util classes like
    2.1 createTokenProcessor() to CRM_Utils_Token
    2.2 Make listTokens() more generic like
 public function listTokens($entity, $className) {
   $tokens = self::createTokenProcessor()->listTokens($className);
   if ($entity == 'contact') {
      $tokens += CRM_Core_SelectValues::contactTokens();
   }
  ...
   return $tokens;
 }

and move to CRM_Utils_Token
2.3 Move CRM_Core_Form_Task_PDFLetterCommon ::renderFromRows($rows, $msgPart, $formValues) to CRM_Util_PDF_Utils

  1. Then delete class CRM_Activity_Form_Task_PDFLetterCommon and CRM_Core_Form_Task_PDFLetterCommon
    ALTERNATIVE: Or we can move the common functionalities to CRM_Core_Form_Task_PDFLetterCommon and leave the native codes to CRM_Contact_Form_Task_PDFLetterCommon

@aydun @eileenmcnaughton @mattwire

@eileenmcnaughton
Copy link
Contributor

Hmm - on first blush I think I like the use of inheritance more than I do moving things to a utils class. But I'm also pretty on the fence. Overall I think this looks like a good patch. Let's see @aydun thoughts...

@aydun
Copy link
Contributor Author

aydun commented Apr 30, 2018

Thanks for your comments @monishdeb and @Eileen

My thinking behind this structure is noted at https://github.com/aydun/civicrm-core/blob/51ed01419a9d4edb82d6d9ff9fdb9373bdc6376e/CRM/Core/Form/Task/PDFLetterCommon.php#L35-L40 - which is the 'alternative' at the end of @monishdeb's comments. I would see that a later move would refactor the common parts into CRM_Core_Form_Task_PDFLetterCommon and then change CRM_Activity_Form_Task_PDFLetterCommon so that it directly extends CRM_Core_Form_Task_PDFLetterCommon not CRM_Contact_Form_Task_PDFLetterCommon

It would be good to get some thoughts from @totten about his plan for migrating to the new token processing to ensure this patch is aligned with that thinking. In particular, https://github.com/aydun/civicrm-core/blob/51ed01419a9d4edb82d6d9ff9fdb9373bdc6376e/CRM/Activity/Tokens.php#L72-L73 seems like it could be improved.

@eileenmcnaughton
Copy link
Contributor

@monishdeb where are you at in your review - is this 'passable but with possible improvements that might be agreed in further discussions, but if that doesn't happen in the short-term we should merge it'? - if so then the merge-ready flag would indicate that - or is it still pending more testing?

@monishdeb
Copy link
Member

monishdeb commented Apr 30, 2018

@eileenmcnaughton @aydun these are the specific changes I want to see in this PR and in my opinion I don't think we can treat this in a separate PR and/or issue (after we merge it) as because we are introducing some new files here and the following changes is appropriate to do it in this PR itself:

  1. Remove CRM_Activity_Form_Task_PDFLetterCommon and use CRM_Core_Form_Task_PDFLetterCommon and move the common functions there so we can inherit these fns in its child classes.
  2. Move some functions to respective classes as mentioned in the 2nd point.
  3. And re createTokenProcessor l like to hear inputs from @totten

From the feature point of view, it working perfectly 👍 So if tagging PR with merge ready implies that it successfully implements the new task BUT need some additional changes on top of it, then should I do the same?

@eileenmcnaughton
Copy link
Contributor

@monishdeb nope - if there are required changes it's not ready for merge-ready.

Note that using traits are an option now if it fits

@totten
Copy link
Member

totten commented May 16, 2018

The PR makes a few changes to the class hierarchy. That's reasonable, but (as a reader) I needed a way to keep track. It helped me to have a summary of the classes/functions at play, so I added that to the description.

Most of the code in CRM_Activity_Form_Task_PDFLetterCommon looks like a pretty faithful application of the TokenProcessor API, so I want to say "You're all good; don't need me here!" But I have a hunch about why you wanted me to check-out the token-handling here. The first couple lines reveal a sort of mixed design wrt token-handling:

screen shot 2018-05-15 at 8 27 13 pm

What's weird is that we call CRM_Contact_Form_Task_PDFLetterCommon::processMessageTemplate, and that calls some older token utilities which overlap with TokenProcessor functionality; then it returns $categories, $messageToken, and $returnProperties... but... all those results are ignored! (The grey coloring indicates unused variables.) We're effectively parsing the template twice. That feels awkward, but in context it serves a purpose: processMessageTemplate() has other functionality (loading/saving templates) which we want.

Overall, here are some of the responsibilities of processMessageTemplate():

  • Reading the template from DB
  • Or reading the template from an uploaded file
  • And optionally auto-saving the template back to DB
  • And parsing the template to get a list of tokens

My reaction to reading that was: "I don't get it; this function does too many different things and lacks a unifying concept with clear pre-conditions/post-conditions." That's a little unfair -- I think there is a core concept, i.e. it's a "Template Selector/Editor Widget" (a mid-level UI building-block akin to a "Billing Block", "Address Block", "EntityRef", "Richtext Editor", or "Hierarchical Multiselect"), but... the design/implementation of the concept has... quirks.

  • A mid-level UI building-block usually has several facets. It makes more sense to model it as a class with various properties+methods (so that each facet is clearly exposed) rather than a static function.
  • The template CRUD widget shouldn't be coupled to the template evaluation logic in this way. (Analogy: It would be really weird if the CiviMail UI had a function which read HTML from ckeditor, saved the draft mailing to the database, passed it to Smarty_Compiler for tokenization, and then... stopped there.)

Of course, I don't think anyone has commented on this... because really... it's not a functional problem. The approach here looks workable -- i.e. it might spend an extra 0.002s on unnecessary parsing, but that doesn't matter. There are a couple unused variables -- but you don't have to bend-over-backward with convoluted mapping logic. The oddity is visually small and doesn't pose any difficulty for the reader. And the oddity is pre-existing.

In an ideal world, we'd probably refactor processMessageTemplate() to have a more appealing/cogent contract. But I think it's OK to approach this generally through the lens of "be consistent with the CRM_Foo_BarCommon::process(&$form) pattern."

(I haven't formed an opinion on whether it's faithful application of CRM_Foo_BarCommon; maybe I can read again in the morning.)

@aydun
Copy link
Contributor Author

aydun commented May 17, 2018

@totten thanks for all your comments here. As you note processMessageTemplate() has multiple responsibilities, not all of which are needed here, but the overhead seems fairly small and is a pre-existing problem.

One part that feels suspect is https://github.com/civicrm/civicrm-core/pull/12012/files#diff-bcf4e4fdbc2537981f5768c8b35b4164 checkActive() avoids unnecessary token evaluations but a list of return conditions doesn't seem good.

@eileenmcnaughton
Copy link
Contributor

@monishdeb can you recheck this now changes have been made to reflect input

@eileenmcnaughton
Copy link
Contributor

@monishdeb looks like this is just needing a final review from you

@seamuslee001
Copy link
Contributor

Jenkins re test this please

@aydun aydun changed the title dev/core#71 Add functionality to create PDF/Word docs from Activity searches WIP dev/core#71 Add functionality to create PDF/Word docs from Activity searches Oct 11, 2018
@aydun
Copy link
Contributor Author

aydun commented Nov 5, 2018

Lots of changes here...

@monishdeb I've changed the class hierarchy so both CRM_Contact_Form_Task_PDFLetterCommon and CRM_Activity_Form_Task_PDFLetterCommon are subclasses of CRM_Core_Form_Task_PDFLetterCommon with common functionality moved up to ..._Core_...

@totten I refactored CRM_Contact_Form_Task_PDFLetterCommon::processMessageTemplate and pushed the template handling into the new parent class so we don't use the old token parser in CRM_Activity_Form_Task_PDFLetterCommon but the functionality of CRM_Contact_Form_Task_PDFLetterCommon should be unchanged.

This includes the schema change to \Civi\Token\TokenProcessor we discussed at the sprint.

I've also changed \Civi\Token\AbstractTokenSubscriber so subclasses can override the determination of active tokens - as used by CRM_Activity_Token to enable variable named tokens (eg {activity.target_N_display_name}

}

$activities = civicrm_api3('Activity', 'get', array(
'id' => array('IN' => $activityIds),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might fail if processing a batch of 25+ messages. Probably needs 'options' => ['limit' => 0]?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also -- the loop above (populating $activityIds) makes perfect sense, but let me put it under a little extra scrutiny because this patch may become an example to inform similar patches to CRM/*/Tokens.php. It'll probably be fairly common to extract a bunch of activityId values (or contributionId or caseId or whatever), and that multiplies out to several 5 SLOC bits of boilerplate. Instead, I'd vote to skip the variable $activityIds and have a helper function like:

    $activities = civicrm_api3('Activity', 'get', array(
      'id' => array('IN' => $e->getTokenProcessor()->getContextValues('activityId')),
      'options' => ['limit' => 0]
    );

https://github.com/civicrm/civicrm-core/compare/master...totten:master-act-tok?expand=1#diff-a2c7ae339db570d160a69bd288516d11R171

(Aside: I've tried a few variations of CRM_Utils_Array::collect() and array_map(), but the magic bits made that more complicated. Adding getContextValues() seemed easier.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - I've added getContextValues(). I still have $activityIds since it is used later in ActivityContact.get

}
else {
$activity = (object) $prefetch['activity'][$row->context['activityId']];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely certain where to to put this comment; maybe this works...

There's a sort of elegance in saying that $activity is the same thing whether it comes from the context['actionSearchResult'] or from $prefetch. Intuitively, these both provide a copy of civicrm_activity.*, so that feels well and good.

The problem is that they're not really interchangeable. For example:

  • actionSearchResult includes activity_type (the label from the OV); however, $prefetch comes from a default Activity.get API call, so it doesn't.
  • actionSearchResult includes case_id; however, $prefetch comes from a default Activity.get API call, so it doesn't.
  • $prefetch includes information about related contacts (given the activityId); however, prefetch() short-circuits in the presence of actionSearchResult. So the contacts are consistently prefetched.
  • There's an expectation in our community that the schema for Activity.get API and for civicrm_activity are allowed to drift apart. actionSearchResult implicitly tracks one while $prefetch implicitly tracks the other.

IMHO, it would get confusing (over time) to maintain with two different functions for loading the data (alterActionScheduleQuery() and prefetch()).

Of course, alterActionScheduleQuery() is in there for a reason -- reducing the number of queries by tapping into the main query. We don't want to issue a new SQL query for every combination of token+recipient. Batch-loading is better.

But I've got this nagging feeling that it would be better to significantly trim down alterActionScheduleQuery():

  • Change: For alterActionScheduleQuery(), we don't need a blanket SELECT e.* or any option-group joins.
  • Change: For prefetch(), get the list of activity IDs from a mix of (a) context['activityId']and/or (b) context['actionSearchResult']->entity_id (provided the mapping type is right).
  • Observe: It always loads activity data the same way.
  • Observe: prefetch() only runs once for a given batch, so we're still batched-loading.
  • Observe: When running the scheduled-reminders, the main query can be a bit thinner (easier to read/debug).
  • Observe: If we optimize prefetch() a bit more (only grab columns that are actually needed), then that optimization applies equally well to both use-cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your thoughtful comments.

I haven't touched alterActionScheduleQuery() or looked at that process in much detail as it was pre-existing and only used for scheduled reminders. The concept of a base SQL query that is passed around for various subscribers to modify for their own needs seems clever, and probably performant, but easy to mess up.

As you point out the schemas for Activity.get API and civicrm_activity may diverge so tracking both could get confusing. Which one do you prefer for these purposes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aydun I think that in terms of Activity.get API vs civicrm_activity - I would probably lean towards the latter. It's one of our less clean apis with all the weird somewhat legacy handling of assignee & target. If we were there with api v4 I'd probably go the other way

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eileenmcnaughton Thanks for the steer - I'll go that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eileenmcnaughton @totten I set off down the path of following civicrm_activity rather than Activity.get. Then I got to the comments on CRM_BAO_Activity::retrieve() noting it should be deprecated and to use the API instead. if we go direct to the table or via the BAO we hit the same issue of bypassing ACL's, so I've stuck with the API, at least for now.

However, by explicitly specifying the return fields, the available tokens are the ones specified in __construct() rather than whatever the API happens to return.

I've made the changes Tim suggested to trim down alterActionScheduleQuery() and move the data gathering to prefetch() so it is consistent between scheduled reminders and other uses.

@aydun
Copy link
Contributor Author

aydun commented Nov 21, 2018

Hmm any clues what those test failures are re Civi\API\Subscriber\WhitelistSubscriberTest::testEach ? Doesn't seem particularly related to this PR.

@eileenmcnaughton
Copy link
Contributor

@aydun this has gotten pretty big - if you can see any smaller extractions of code moves that are non functional it might be worth breaking off a couple of smaller prepatory PRs & we can get those merged

This was referenced Nov 29, 2018
@aydun
Copy link
Contributor Author

aydun commented Nov 29, 2018

Hi @eileenmcnaughton I've extracted 3 new PR's. If we can get those merged I can rebase the rest.

@totten - you wanted to give CRM_Activity_Tokens some scrutiny as it might be used as an example for other CRM/*/Tokens.php I think I have incorporated all your comments but would appreciate you looking again.

This introduces eg {activity.target_N_display_name} The underscore is overloaded here and a better syntax might be {activity.target:N:display_name} ... but tokens historically have not allowed colons. What are you thoughts about expanding the characters allowed in token names?

@aydun
Copy link
Contributor Author

aydun commented Nov 29, 2018

FYI - this is now working on a production site.

@aydun
Copy link
Contributor Author

aydun commented Mar 26, 2019

@eileenmcnaughton As you suggested, I have extracted the new base class into a separate PR. #13892
I'll rebase this again once that is merged.

@aydun
Copy link
Contributor Author

aydun commented Mar 27, 2019

Rebased and ready for review

@eileenmcnaughton
Copy link
Contributor

@aydun I pulled out a few more changes - #13967 - we are doing some big formatting changes which would have made this bit go stale so I figured I could make the impact on this PR more positive by getting an extraction merged than by just forcing staleness

I didn't do the bit about setting properties - TBH I wasn't clear that it was saving additional queries so I left it out for now

@aydun
Copy link
Contributor Author

aydun commented Apr 8, 2019

@eileenmcnaughton Many thanks for extracting out those changes. I've rebased again to take account of that.

I agree the properties don't save much for basic or custom tokens but it seems worthwhile for the special tokens, so for consistency I have kept that.

@aydun
Copy link
Contributor Author

aydun commented Apr 11, 2019

Error looks unrelated.

Jenkins test this please.

@totten
Copy link
Member

totten commented Jun 14, 2019

(CiviCRM Review Template WORD-1.2)

  • General standards
    • (r-explain) Pass
    • (r-user) Pass
    • (r-doc) Undecided: This has been a big project, so it feels like it merits some kind of documentation, although it's maybe fairly clear from a user POV. (EDIT) Talked to @demeritcowboy F2F about the review he's doing, and the special-token functionality would certainly merit explanation.
    • (r-run) Undecided: Not assessed
  • Developer standards
    • (r-tech) Pass: This is generally new code without technical implications for existing code. There are some updated event-listeners that share code-paths with other use-cases, but those have test coverage. 👍
    • (r-code) Pass: General style looks nice. 👍 I was a little concerned about the complexity of some of the new tokens, but it's pretty proportionate. The underlying functionality of target_N_ is fairly powerful/sophisticated, so the complexity here qualifies as essential-complexity.
    • (r-maint) Pass: Adds new test coverage for the new tokens.
    • (r-test) Undecided: The last test run was good, but I'd suggest another test-run before merging.

@eileenmcnaughton
Copy link
Contributor

@demeritcowboy you can get out of me talking you into a unit test today by testing this if you would like :-) It has had code review so just needs r-run

@demeritcowboy
Copy link
Contributor

jenkins test this please

@eileenmcnaughton
Copy link
Contributor

@aydun looks like we moved the style goalposts - @demeritcowboy you can assume for now it's passing since it did pass before

@demeritcowboy
Copy link
Contributor

@eileenmcnaughton I really just wanted to generate a test site. I can test locally just wanted to avoid any windows weirdness.

Create CRM_Actvity_Form_Task_PDFLetterCommon extending CRM_Core_Form_Task_PDFLetterCommon

CRM_Activity_Tokens: add source, target and assignee tokens

Eg: {activity.target_N_display_name}
- N can be substituted for a number to show the details of the nth activity target contact
eg: {activity.target_1_display_name} is the display name of the first target
For ease of use, contacts are numbered from 1.
If the literal N is used or is replaced by 0, it is treated as 1.

Slim down alterActionScheduleQuery() and move most data fetching to prefetch()

Add tests for Activity PDF Letter
@aydun aydun force-pushed the activity_pdf_71 branch 2 times, most recently from e2515e4 to 2dfc15f Compare June 15, 2019 15:07
@demeritcowboy
Copy link
Contributor

  • General standards
    • [r-explain] PASS
    • [r-user] Issue
      • In the dropdown list of tokens, the word "target" is an internal word. Users would call it "With Contact". Similarly "Source" is called "Added By".
    • [r-doc] Issue
      • The "N" part needs some documentation. But what's the use-case for example to just output "With Contact 2" only from a series of selected activities? And what does 2 mean since they aren't strictly ordered in the database? The default if you leave it at "N", which the dropdown inserts verbatim, is that it picks the first one, which while arbitrary will cover the majority of activities, where there is only one. So it's still useful as-is that way.
      • Is there a way using this to just output for example "display name for all the with contacts"? (Although other than name wanting to concatenate a field for all with contacts might not make any sense.)
      • So it seems like the "N" part maybe isn't even necessary and maybe should just always be "first one"? Could do a fuller treatment of "N" via an extension?
    • [r-run] Issue
      • After selecting an activity and choosing the action from the actions dropdown I get Warning: count(): Parameter must be an array or an object that implements Countable in CRM_Core_Form_Task_PDFLetterCommon::buildQuickForm() (line 150 of blah\blah\CRM\Core\Form\Task\PDFLetterCommon.php).
      • Tokens for custom fields for the source don't seem to work, just displaying the token itself (e.g. {activity.source_custom_8})
        • Notice: Undefined property: stdClass::$custom_8 in CRM_Activity_Tokens->evaluateToken() (line 295 of blah\blah\CRM\Activity\Tokens.php).
        • Warning: Invalid argument supplied for foreach() in Civi\Token\TokenRow->fill() (line 223 of blah\blah\Civi\Token\TokenRow.php).
    • I tested PDF. When I tried to test MS Word it came back with File Not Found. I don't think it's my windows laptop because MS Word works for the contact search print/merge function.
  • Developer standards
    • [r-tech] PASS
    • [r-code] PASS
    • [r-maint] PASS Optionally suggest adding test for source custom field as per above.
    • [r-test] PASS Conditional pass based on previous runs.

@eileenmcnaughton
Copy link
Contributor

@aydun I'm keen to get this merged - if there are any parts that are mentioned above that can be left out & put to a follow up I'd go for that

@eileenmcnaughton
Copy link
Contributor

I have opened #14662 in attempt to get everything except the part under discussion (activity contact tokens) merged #14662

Oh I fixed

After selecting an activity and choosing the action from the actions dropdown I get Warning: count(): Parameter must be an array or an object that implements Countable in CRM_Core_Form_Task_PDFLetterCommon::buildQuickForm() (line 150 of blah\blah\CRM\Core\Form\Task\PDFLetterCommon.php).

@eileenmcnaughton
Copy link
Contributor

eileenmcnaughton commented Jun 28, 2019

"Tokens for custom fields for the source don't seem to work" - they are excluded from my cut down version (punted)

@eileenmcnaughton
Copy link
Contributor

On thee 3rd r-run issue -

I tested PDF. When I tried to test MS Word it came back with File Not Found. I don't think it's my windows laptop because MS Word works for the contact search print/merge function.

  • I removed the options for things other than pdf.

I think this means I have removed all the things that are not working properly from the version I've put up

@mlutfy
Copy link
Member

mlutfy commented Dec 2, 2019

jenkins, test this please

@eileenmcnaughton
Copy link
Contributor

@aydun looks like @mattwire has moved the rest of this too #16200 after the current one just got merged - closing this

@mattwire
Copy link
Contributor

mattwire commented Jan 3, 2020

See https://lab.civicrm.org/mattwire/emailapi/tree/newtokenprocessor for a useful way to try it out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants