CiviCRM Core issueshttps://lab.civicrm.org/dev/core/-/issues2023-06-20T13:19:14Zhttps://lab.civicrm.org/dev/core/-/issues/4366Vcards - tokens & other rendering2023-06-20T13:19:14ZeileenVcards - tokens & other renderingIt turns out we render html vcards for event locations in our emails - eg.
<div class="location vcard"><span class="adr"><span class="street-address">8 Baker Street</span><br />
<span class="extended-address">Upstairs</span><br />
<span...It turns out we render html vcards for event locations in our emails - eg.
<div class="location vcard"><span class="adr"><span class="street-address">8 Baker Street</span><br />
<span class="extended-address">Upstairs</span><br />
<span class="locality">London</span>,<br />
</span></div>
I'm not sure if this is
a) good - it's super helpful or
b) bad - it's copy & paste from our front end pages & lands us in the spam folder
I am hoping people will weigh in on the question of whether vcard html in email is desirable or not because making code do something silly more effectively is about 78 on my list of priorities.
If the former then I think we want to do it in a more standardised way than the dragon-code that currently does it.
I found a library https://github.com/jeroendesloovere/vcard
@colemanw also suggested an api calculated field (similar to contribution.paid_amount) although it could be tricky - see https://github.com/civicrm/civicrm-core/pull/26296#issuecomment-1561001040
We would want it to be an option for the token - ie regardless of whether there is a use case for the vcard in emails there is a use case for use to format addresses with line breaks.https://lab.civicrm.org/dev/core/-/issues/4364Afform: Adding forms to menu is not compatible with Customize Navigation Menu2023-10-19T23:44:23ZlarsssandergreenAfform: Adding forms to menu is not compatible with Customize Navigation MenuIf you add a menu item for a form directly in the form, it shows up sort of where you want it (though the interface to set the order is pretty unhelpful, because you basically are guessing what the weight of existing items in the menu mi...If you add a menu item for a form directly in the form, it shows up sort of where you want it (though the interface to set the order is pretty unhelpful, because you basically are guessing what the weight of existing items in the menu might be). However, if you later go to Customize Navigation Menu, you can move the menu item you created around and it looks like it works and it will work for a while, but then later, it will move back to the location and weight set in the form.
This is confusing for users and frustrating if you don't know what's going on. Seems like we need to have just one way to edit the menu. Maybe it makes sense to simply remove the add to menu option from forms and let users add the menu item manually? Or alternately, we need a way for the menu location and weight to only be used on inserting the menu item and to be uneditable in the form afterwards, maybe with a help text that tells you to edit this directly in the menu.https://lab.civicrm.org/dev/core/-/issues/4362civiimport failures with `cv`2023-11-08T00:50:25Zaydunsaidan.saunders@squiffle.ukciviimport failures with `cv`Overview
----------------------------------------
The `civiimport` extension causes log "API Request Authorization failed" messages when using `cv`
Reproduction steps
----------------------------------------
1. On a system where the `ci...Overview
----------------------------------------
The `civiimport` extension causes log "API Request Authorization failed" messages when using `cv`
Reproduction steps
----------------------------------------
1. On a system where the `civiimport` extension is not enabled: run `cv en civiimport`
Also, when installed run `cv flush`
Current behaviour
----------------------------------------
In the log file, note the backtrace:
```
Jun 15 11:26:51 [debug] $API Request Authorization failed = #0 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(153): CRM_Core_Error::backtrace("API Request Authorization failed", TRUE)
#1 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Generic/AbstractAction.php(250): Civi\API\Kernel->runRequest(Object(Civi\Api4\Generic\DAOGetAction))
#2 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php(221): Civi\Api4\Generic\AbstractAction->execute()
#3 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php(197): Civi\Api4\Event\Subscriber\ImportSubscriber::getImportForms()
#4 [internal function](): Civi\Api4\Event\Subscriber\ImportSubscriber::on_civi_afform_get(Object(Civi\Core\Event\GenericHookEvent), "civi.afform.get", Object(Civi\Core\UnoptimizedEventDispatcher))
#5 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/Event/ServiceListener.php(53): call_user_func_array((Array:2), (Array:3))
#6 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(251): Civi\Core\Event\ServiceListener->__invoke(Object(Civi\Core\Event\GenericHookEvent), "civi.afform.get", Object(Civi\Core\UnoptimizedEventDispatcher))
#7 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(73): Symfony\Component\EventDispatcher\EventDispatcher->callListeners((Array:2), "civi.afform.get", Object(Civi\Core\Event\GenericHookEvent))
#8 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/CiviEventDispatcher.php(260): Symfony\Component\EventDispatcher\EventDispatcher->dispatch(Object(Civi\Core\Event\GenericHookEvent), "civi.afform.get")
#9 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/afform/core/Civi/Api4/Action/Afform/Get.php(40): Civi\Core\CiviEventDispatcher->dispatch("civi.afform.get", Object(Civi\Core\Event\GenericHookEvent))
#10 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Generic/BasicGetAction.php(52): Civi\Api4\Action\Afform\Get->getRecords()
#11 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Provider/ActionObjectProvider.php(72): Civi\Api4\Generic\BasicGetAction->_run(Object(Civi\Api4\Generic\Result))
#12 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(158): Civi\Api4\Provider\ActionObjectProvider->invoke(Object(Civi\Api4\Action\Afform\Get))
#13 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Generic/AbstractAction.php(250): Civi\API\Kernel->runRequest(Object(Civi\Api4\Action\Afform\Get))
#14 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/afform/core/afform.php(399): Civi\Api4\Generic\AbstractAction->execute()
#15 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook.php(272): afform_civicrm_alterMenu((Array:498))
#16 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook/WordPress.php(136): CRM_Utils_Hook->runHooks((Array:33), "civicrm_alterMenu", 1, (Array:498), NULL, NULL, NULL, NULL, NULL)
#17 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/CiviEventDispatcher.php(307): CRM_Utils_Hook_WordPress->invokeViaUF(1, (Array:498), NULL, NULL, NULL, NULL, NULL, "civicrm_alterMenu")
#18 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(251): Civi\Core\CiviEventDispatcher::delegateToUF(Object(Civi\Core\Event\GenericHookEvent), "hook_civicrm_alterMenu", Object(Civi\Core\UnoptimizedEventDispatcher))
#19 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(73): Symfony\Component\EventDispatcher\EventDispatcher->callListeners((Array:1), "hook_civicrm_alterMenu", Object(Civi\Core\Event\GenericHookEvent))
#20 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/CiviEventDispatcher.php(260): Symfony\Component\EventDispatcher\EventDispatcher->dispatch(Object(Civi\Core\Event\GenericHookEvent), "hook_civicrm_alterMenu")
#21 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook.php(168): Civi\Core\CiviEventDispatcher->dispatch("hook_civicrm_alterMenu", Object(Civi\Core\Event\GenericHookEvent))
#22 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook.php(661): CRM_Utils_Hook->invoke((Array:1), (Array:498), NULL, NULL, NULL, NULL, NULL, "civicrm_alterMenu")
#23 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Menu.php(78): CRM_Utils_Hook::alterMenu((Array:498))
#24 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Menu.php(180): CRM_Core_Menu::xmlItems(TRUE)
#25 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Menu.php(294): CRM_Core_Menu::items(TRUE)
#26 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Invoke.php(389): CRM_Core_Menu::store()
#27 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Extension/Manager.php(319): CRM_Core_Invoke::rebuildMenuAndCaches(TRUE)
#28 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/api/v3/Extension.php(42): CRM_Extension_Manager->install((Array:4))
#29 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Provider/MagicFunctionProvider.php(89): civicrm_api3_extension_install((Array:3))
#30 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(158): Civi\API\Provider\MagicFunctionProvider->invoke((Array:8))
#31 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(81): Civi\API\Kernel->runRequest((Array:8))
#32 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/api/api.php(22): Civi\API\Kernel->runSafe("Extension", "install", (Array:3))
#33 phar:///home/XXX/private/bin/cv/src/Command/BaseCommand.php(39): civicrm_api("Extension", "install", (Array:3))
#34 phar:///home/XXX/private/bin/cv/src/Command/ExtensionEnableCommand.php(68): Civi\Cv\Command\BaseCommand->callApiSuccess(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput), "Extension", "install", (Array:3))
#35 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Command/Command.php(127): Civi\Cv\Command\ExtensionEnableCommand->execute(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#36 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Application.php(637): Cvphar\Symfony\Component\Console\Command\Command->run(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#37 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Application.php(190): Cvphar\Symfony\Component\Console\Application->doRunCommand(Object(Civi\Cv\Command\ExtensionEnableCommand), Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#38 phar:///home/XXX/private/bin/cv/src/Application.php(66): Cvphar\Symfony\Component\Console\Application->doRun(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#39 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Application.php(101): Civi\Cv\Application->doRun(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#40 phar:///home/XXX/private/bin/cv/src/Application.php(32): Cvphar\Symfony\Component\Console\Application->run()
#41 phar:///home/XXX/private/bin/cv/bin/cv(28): Civi\Cv\Application::main("phar:///home/XXX/private/bin/cv/bin")
#42 /home/XXX/private/bin/cv(14): require("phar:///home/XXX/private/bin/cv/bin/cv")
#43 {main}
```
For `cv flush`:
```
Jun 15 11:35:11 [debug] $API Request Authorization failed = #0 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(153): CRM_Core_Error::backtrace("API Request Authorization failed", TRUE)
#1 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Generic/AbstractAction.php(250): Civi\API\Kernel->runRequest(Object(Civi\Api4\Generic\DAOGetAction))
#2 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php(221): Civi\Api4\Generic\AbstractAction->execute()
#3 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php(197): Civi\Api4\Event\Subscriber\ImportSubscriber::getImportForms()
#4 [internal function](): Civi\Api4\Event\Subscriber\ImportSubscriber::on_civi_afform_get(Object(Civi\Core\Event\GenericHookEvent), "civi.afform.get", Object(Civi\Core\UnoptimizedEventDispatcher))
#5 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/Event/ServiceListener.php(53): call_user_func_array((Array:2), (Array:3))
#6 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(251): Civi\Core\Event\ServiceListener->__invoke(Object(Civi\Core\Event\GenericHookEvent), "civi.afform.get", Object(Civi\Core\UnoptimizedEventDispatcher))
#7 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(73): Symfony\Component\EventDispatcher\EventDispatcher->callListeners((Array:2), "civi.afform.get", Object(Civi\Core\Event\GenericHookEvent))
#8 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/CiviEventDispatcher.php(260): Symfony\Component\EventDispatcher\EventDispatcher->dispatch(Object(Civi\Core\Event\GenericHookEvent), "civi.afform.get")
#9 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/afform/core/Civi/Api4/Action/Afform/Get.php(40): Civi\Core\CiviEventDispatcher->dispatch("civi.afform.get", Object(Civi\Core\Event\GenericHookEvent))
#10 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Generic/BasicGetAction.php(52): Civi\Api4\Action\Afform\Get->getRecords()
#11 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Provider/ActionObjectProvider.php(72): Civi\Api4\Generic\BasicGetAction->_run(Object(Civi\Api4\Generic\Result))
#12 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(158): Civi\Api4\Provider\ActionObjectProvider->invoke(Object(Civi\Api4\Action\Afform\Get))
#13 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Api4/Generic/AbstractAction.php(250): Civi\API\Kernel->runRequest(Object(Civi\Api4\Action\Afform\Get))
#14 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/afform/core/afform.php(399): Civi\Api4\Generic\AbstractAction->execute()
#15 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook.php(272): afform_civicrm_alterMenu((Array:498))
#16 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook/WordPress.php(136): CRM_Utils_Hook->runHooks((Array:33), "civicrm_alterMenu", 1, (Array:498), NULL, NULL, NULL, NULL, NULL)
#17 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/CiviEventDispatcher.php(307): CRM_Utils_Hook_WordPress->invokeViaUF(1, (Array:498), NULL, NULL, NULL, NULL, NULL, "civicrm_alterMenu")
#18 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(251): Civi\Core\CiviEventDispatcher::delegateToUF(Object(Civi\Core\Event\GenericHookEvent), "hook_civicrm_alterMenu", Object(Civi\Core\UnoptimizedEventDispatcher))
#19 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/vendor/symfony/event-dispatcher/EventDispatcher.php(73): Symfony\Component\EventDispatcher\EventDispatcher->callListeners((Array:1), "hook_civicrm_alterMenu", Object(Civi\Core\Event\GenericHookEvent))
#20 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/Core/CiviEventDispatcher.php(260): Symfony\Component\EventDispatcher\EventDispatcher->dispatch(Object(Civi\Core\Event\GenericHookEvent), "hook_civicrm_alterMenu")
#21 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook.php(168): Civi\Core\CiviEventDispatcher->dispatch("hook_civicrm_alterMenu", Object(Civi\Core\Event\GenericHookEvent))
#22 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Utils/Hook.php(661): CRM_Utils_Hook->invoke((Array:1), (Array:498), NULL, NULL, NULL, NULL, NULL, "civicrm_alterMenu")
#23 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Menu.php(78): CRM_Utils_Hook::alterMenu((Array:498))
#24 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Menu.php(180): CRM_Core_Menu::xmlItems(TRUE)
#25 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Menu.php(294): CRM_Core_Menu::items(TRUE)
#26 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/CRM/Core/Invoke.php(389): CRM_Core_Menu::store()
#27 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/api/v3/System.php(33): CRM_Core_Invoke::rebuildMenuAndCaches(FALSE, FALSE)
#28 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Provider/MagicFunctionProvider.php(89): civicrm_api3_system_flush((Array:2))
#29 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(158): Civi\API\Provider\MagicFunctionProvider->invoke((Array:8))
#30 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/Civi/API/Kernel.php(81): Civi\API\Kernel->runRequest((Array:8))
#31 /home/XXX/www/www/wp-content/plugins/civicrm/civicrm/api/api.php(22): Civi\API\Kernel->runSafe("System", "flush", (Array:2))
#32 phar:///home/XXX/private/bin/cv/src/Command/BaseCommand.php(39): civicrm_api("System", "flush", (Array:2))
#33 phar:///home/XXX/private/bin/cv/src/Command/FlushCommand.php(28): Civi\Cv\Command\BaseCommand->callApiSuccess(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput), "System", "flush", (Array:2))
#34 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Command/Command.php(127): Civi\Cv\Command\FlushCommand->execute(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#35 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Application.php(637): Cvphar\Symfony\Component\Console\Command\Command->run(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#36 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Application.php(190): Cvphar\Symfony\Component\Console\Application->doRunCommand(Object(Civi\Cv\Command\FlushCommand), Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#37 phar:///home/XXX/private/bin/cv/src/Application.php(66): Cvphar\Symfony\Component\Console\Application->doRun(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#38 phar:///home/XXX/private/bin/cv/vendor/symfony/console/Application.php(101): Civi\Cv\Application->doRun(Object(Cvphar\Symfony\Component\Console\Input\ArgvInput), Object(Cvphar\Symfony\Component\Console\Output\ConsoleOutput))
#39 phar:///home/XXX/private/bin/cv/src/Application.php(32): Cvphar\Symfony\Component\Console\Application->run()
#40 phar:///home/XXX/private/bin/cv/bin/cv(28): Civi\Cv\Application::main("phar:///home/XXX/private/bin/cv/bin")
#41 /home/XXX/private/bin/cv(14): require("phar:///home/XXX/private/bin/cv/bin/cv")
#42 {main}
```
Both get to `/home/XXX/www/www/wp-content/plugins/civicrm/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php(197): Civi\Api4\Event\Subscriber\ImportSubscriber::getImportForms()`
Expected behaviour
----------------------------------------
No errors.
Note that if you run `cv ext import -U admin` or `cv flush -U admin`, these run without errors but are not the normal invocations. I'm not clear if this should be fixed in `cv` or `civiimport`.
`cv` mostly lets you do 'adminy things' without specifying a user. Some actions (like `cv scr`, `cv ev`) need a user but `cv flush` and `cv en`, `cv dis` have not needed this.
Environment information
----------------------------------------
* __CiviCRM:__ 5.62.0 <!-- Not new - issue exists earlier as well. -->
* __PHP:__ 7.4.32
* __cv:__ v0.3.42
Comments
----------------------------------------
ping @tottenhttps://lab.civicrm.org/dev/core/-/issues/4361Add Pay now link to Invoice template2023-06-28T06:58:22ZlarsssandergreenAdd Pay now link to Invoice templateIt would be nice to have the link to pay online included in the Invoice template. Currently, a user would have to guess this is possible, find the correct link on SE and add it to the template (then maintain it as the template is updated...It would be nice to have the link to pay online included in the Invoice template. Currently, a user would have to guess this is possible, find the correct link on SE and add it to the template (then maintain it as the template is updated).
I think if default_invoice_page is set, we could simply include a Pay Now link in the Payment Advice section (which is only shown if the Contribution is pending and pay later). If an org doesn't want to include a link for online payment, they can simply leave default_invoice_page empty (Edit: They cannot currently do this for newer installs, will have to make it possible). I think it would be very rare for an org to want to have a default_invoice_page for payments from the User Dashboard, but not to include a link for online payments in invoices (and in that case, they can edit the template).
Will do this and add docs if supported.https://lab.civicrm.org/dev/core/-/issues/4358Verbiage changes on Find Groups screen2023-08-17T17:50:18ZyashodhaVerbiage changes on Find Groups screenPropose to make following changes on Find Groups.
- Change Type to Usage. We now have Type : Access Control /Mailing List
and Group Type as Normal Group / Smart Group. The type is more of a usage (used for Access Control /Mailing List)...Propose to make following changes on Find Groups.
- Change Type to Usage. We now have Type : Access Control /Mailing List
and Group Type as Normal Group / Smart Group. The type is more of a usage (used for Access Control /Mailing List), so make the changes all across the Settings screens in addition to search results/filter.
- Change Normal Group to Regular Group. Well because there's nothing abnormal about the smart groups :)yashodhayashodhahttps://lab.civicrm.org/dev/core/-/issues/4357Timezone with Drupal 9, CiviCRM entity and Views2023-12-23T00:55:25Z5knotsTimezone with Drupal 9, CiviCRM entity and ViewsOverview
----------------------------------------
We are working with grants, where fields like "money transferred" exist. This field is stored in the database as a "date-only" value (screenshot). However, Drupal interprets the date as a...Overview
----------------------------------------
We are working with grants, where fields like "money transferred" exist. This field is stored in the database as a "date-only" value (screenshot). However, Drupal interprets the date as a datetime field and, presumably, sets the time to 12 noon, but a day before. This leads to inconsistent dates in Drupal Views outputs. Is there a known workaround to avoid this issue? Currently, I have set a fixed 12-hour timezone difference for the View, which works but is not an ideal solution.
Reproduction steps
----------------------------------------
1. Install civicrm entity and enable grants
1. go to Drupal Views and create a new view based on grants.
1. include fields like 'money transferred'
Current behaviour
----------------------------------------
Drupal interprets the date as a **datetime** field.
Both attached screenshots are from the same entry.
![Bildschirmfoto_2023-06-13_um_14.41.49](/uploads/6b7df46f1fdd9354238c4e4e90ce0dec/Bildschirmfoto_2023-06-13_um_14.41.49.png)
![Bildschirmfoto_2023-06-13_um_14.43.32](/uploads/e324ce91d7fe2a48a0ce84c4bb580589/Bildschirmfoto_2023-06-13_um_14.43.32.png)
Expected behaviour
----------------------------------------
Drupal interprets the date as a **date** field.
Environment information
----------------------------------------
D9.5, CiviCRM 5.62, civicrm_entity /latesthttps://lab.civicrm.org/dev/core/-/issues/4356(Exploration) Replace AngularJS with `$XXX`2023-07-28T00:09:29Ztotten(Exploration) Replace AngularJS with `$XXX`(*This document was written for a technical audience -- people who are familiar with many technologies, who want to bring them to Civi, and who find themselves asking, "Why can't we have nice things?" It was initially drafted as private ...(*This document was written for a technical audience -- people who are familiar with many technologies, who want to bring them to Civi, and who find themselves asking, "Why can't we have nice things?" It was initially drafted as private document;
then shared during the Manchester 2022 sprint as a [gist](https://gist.github.com/totten/5c34e3885a4fe7002f990e09395b4294). By request, it's now copied to the issue-tracker so that we have a more familiar discussion-space and reference-id. I've made
a few small edits and documented one more of the challenges, but the substance is largely the same.*)
# Table of Contents
[[_TOC_]]
# Preliminaries
## Why replace AngularJS?
* The upstream "AngularJS" project (*a Javascript framework*) re-made itself as "Angular" (*a Typescript framework*).
* This reflects important changes in the platform, and it is not a straight-forward upgrade. Adopting newer "Angular" versions should be approached as a *migration*.
* The upstream "AngularJS" project has been proclaiming: "We don't like our JS product anymore. Don't use that."
* One original goal of adopting "AngularJS" was to get some bandwagon benefits. Those benefits are no longer there.
## What is "$XXX"?
`$XXX` is a stand-in for "Angular 14 or ReactJS or some other client-side page-rendering system". The specific value is a matter for consideration.
Some migration issues are generally applicable to any alternative, and we may use `$XXX` as stand-in (because it gets tiresome to write "Angular 14 or ReactJS or some other client-side page-rendering system").
## What is CiviCRM?
For purposes of this evaluation, CiviCRM is an ecosystem -- a collection of packages developed and maintained by many people. It includes a significant core package and a significant set of extensions. If we adopt `$XXX`, then we ae encouraging this ecosystem to write their screens and UI components with `$XXX`.
Today, CiviCRM's ecosystem is quite varied:
* You have `civicrm-core` -- which includes the framework, several common business modules, APIs, and various UIs for admins+staff+consumers.
* You have extensions like CiviVolunteer or CiviGrant -- which define new business modules (including their APIs and UIs).
* You have extensions like CiviRules or Search Kit -- which build generic functionality (that works with many business modules).
* You have extensions like Mosaico or CiviDiscount -- which significantly improve the features of an existing business-module.
* You have extensions like AngularProfiles or Email API -- which add extra building-blocks or fill-in gaps.
* You have bespoke extensions for specific sites -- extensions which fine-tune screens, workflows, settings, for a specific organizational context.
Each package may define some mix of:
* Redistributed artifacts (like CSS files, APIv3 files, APIv4 files, Smarty templates, etc) curated by developers.
* Local artifacts (like Profiles and SavedSearches and Webforms and Afforms) curated by administrators for a specific site (with support from the extensions).
These packages are distributed in various ways, eg:
* Published as part of `civicrm-core`
* Published in a public directory for impromptu download/installation
* Organized as in-house collections
Is this the optimal shape for the ecosystem? I'm not sure how you would answer that.
Instead, for this discussion, I simply accept this shape for the ecosystem. CiviCRM's ecosystem should continue to have similar kinds of packages and similar kinds of distribution -- even if some specific packages change, and even if it adopts another tool like `$XXX`. This shape is not perfect or static - but it is realistic and representative.
# Challenges
Perhaps we just drop-in some new files for the latest version of Angular? Or, failing that, drop-in ReactJS? Surely, at the end of the day, these are JS files used by the browser. You just need to put them on a web-server. The browser will run them, and that's it. What's wrong with that?
Let's break-down a few specific challenges.
* Linker-Loader and Modularity
* Angular 14 and TypeScript
* ReactJS and JSX
The challenges can be *addressed* -- the question is what *trade-offs, costs, or compromises* we accept.
## Linker-Loader and Modularity
### Definition
Sane software is built in pieces. To run the software, you have to put the pieces together. This requires a linking or loading mechanism.
Just to clarify what I mean, let's use a basic example. Here we have some classes for an application where the user draws rectangles on a canvas:
```js
// FILE: Canvas.js
class Canvas {
constructor() {
// A list of visual objects to display in this presentation
this.objects = [];
}
addRectangle(x, y, width, height) {
this.objects.push({
pos: new Coordinate(x, y),
obj: new Rectangle(width, height)
});
}
}
```
```js
// FILE: Rectangle.js
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
```
```js
// FILE: Coordinate.js
class Coordinate {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
```
N.B. To use `Canvas`, you must also use `Rectangle` and `Coordinate`. There must be a mechanism to say: "*We are loading `Canvas.js` and also `Rectangle.js` plus `Coordinate.js`.*"
If you're reading this, then I'm sure you're already familiar with several such mechanisms, though they go by many names. Perhaps you've encountered PHP's "autoloader"; Javascript's "webpack"; bash's "PATH"; C's "static-linking" and "libtool". All of these address the need to combine/resolve related pieces of software.
The design of the link-load mechanism can have effects on:
* Performance - Load times, download sizes, cache efficacy, update frequency, etc. (*A link-load mechanism that performs nicely for small apps might perform badly for large apps, and vice-versa.*)
* Workflow - The ease or difficulty of installing, developing, customizing, upgrading, etc.
* Modularity - How you design/frame/distribute the packages of the system.
In the phrase "link-load", I wish to evoke the general mechanism of "getting the stuff we need" -- with an understanding that different systems have different phrases, tools, and flows.
### What kind of linking/loading does CiviCRM require?
Recall that the CiviCRM ecosystem has a mix of extensions providing different kinds of value (*business modules, APIs, UIs, etc*) with various artifacts (*CSS files, APIv4 files, Quickform files*). If we adopt `$XXX`, then `$XXX` will provide more kinds of artifacts (*JS-based pages; Angular components; React components; etc*). You should be able to define these in Civi extensions. Thus, you might create files like:
```
## Angular component defined in core on Drupal 9
/var/www/vendor/civicrm/civicrm-core/ang/*.component.ts
## React component defined in an extension on Drupal 7
/var/www/sites/default/files/ext/myextension/react/*.jsx
```
The linker/loader mechanism used for `$XXX` in CiviCRM should be able to:
* Load screens defined by core+extensions.
* Load components defined by core+extensions (across extension boundaries).
* Load transitive dependencies required by the screens+components in core+extensions (across extension boundaries).
### What does linking/loading look like in Javascript?
Javascript is a big ecosystem! And it's been around for 25 years. We will not try to discuss all the techniques of the many applications/frameworks/tools. Rather, let's note a couple relevant practices - the baseline; the current mechanisms in CiviCRM; and the mechanisms favored in contemporary Angular+React ecosystems.
First, as a baseline, the traditional mechanism (*available to all web developers since 1995*) is to write HTML: you create a webpage with a list of `<script>` tags containing URLs of all your JS files.
```html
<head>
<script type="text/javascript" src="./js/Rectangle.js"></script>
<script type="text/javascript" src="./js/Coordinate.js"></script>
<script type="text/javascript" src="./js/Canvas.js"></script>
</head>
```
This is (roughly) how JS files get into CiviCRM's PHP screens - except that the `<script>` tags are written as PHP statements (`addScriptFile($ext, $file)`). But it's similar: it's a top-level list of JS files, and they're printed as a list of `<script>` tags in the main HTML document. On a case-by-case basis, some screens make extra calls to `addScriptFile`/`<script>`.
CiviCRM's AngularJS screens use another mechanism -- a *dependency-graph*. It works as follows:
* Each major Angular module has an explicit metadata file (`*.ang.php`) with a list of inputs and outputs (components/tags/etc).
* If a site-builder/developer makes a custom form (`*.aff.html`), then we scan the HTML and infer its dependencies (based on the tags and attributes).
* When a user requests an Angular-based screen (eg `civicrm/admin/search`), then we use this metadata to identify dependencies and send them all as a bundle.
In contemporary Angular and ReactJS ecosystems, it is also conventional to use a dependency-graph -- but now based on *ES6 modules*. ECMAScript 6 defines *a language-level standard* with `import`/`export` statements. For example, `Canvas.js` could be updated to:
```js
// FILE: Canvas.js
import { Coordinate } from 'Coordinate.js';
import { Rectangle } from 'Rectangle.js';
export class Canvas {
constructor() {
// A list of visual objects to display in this presentation
this.objects = [];
}
addRectangle(x, y, width, height) {
this.objects.push({
pos: new Coordinate(x, y),
obj: new Rectangle(width, height)
});
}
}
```
For pure JS development, this represents a significant improvement over the baseline -- you do not need to maintain a global list of scripts. You don't need custom metadata (like `*.ang.php`). You describe the JS dependencies within the JS files. And unlike Civi's AngularJS loader, this mechanism is a widely known standard. ES6 modules have become nearly ubiquitous in documentation for contemporary JS libraries.
### Challenges of linking/loading for Javascript
ES6 `import`s are part of the language standard, but you cannot take this mechanism for granted. There are multiple implementations with important differences. To see this, let's do an example.
Suppose you are writing an extension like "Mosaico" which builds on top of CiviMail. You need to use a (Angular/React/etc) component from CiviMail. So you `import` it:
```js
import ReceipientSelector from '../../../sites/all/modules/civicrm/components/Mailing/RecipientSelector.js';
const MosaicoEditor = () => <div>....<ReceipientSelector />...</div>;
export default MosaicoEditor;
```
This import (*with a physical path*) is compatible with most implementations of `import`, but the path itself is untenable -- it will only work on (say) 50% of CiviCRM deployments. For broad support, you need to hide some of these physical details and use a simpler (logical) path -- such as:
```diff
-import ReceipientSelector from '../../../sites/all/modules/civicrm/components/Mailing/RecipientSelector.js';
+import ReceipientSelector from 'civicrm/components/Mailing/RecipientSelector.js';
```
This can be done - and I suspect that most pure-JS apps benefit from simpler expressions. However, this depends on *the particular implementation* of `import`. Implementations fall into a few major groups:
* _Browsers_: Since [~2019](https://caniuse.com/mdn-javascript_operators_import), most major web-browsers have had support for `import`s with physical paths. However, support for [logical paths ("import maps")](https://github.com/WICG/import-maps) is [gradually rolling out](https://caniuse.com/import-maps). At time of writing, Chrome has support; Firefox requires an experimental opt-in (`about:config`); and Safari has no support. ([There may be a polyfill](https://github.com/systemjs/systemjs/blob/main/docs/import-maps.md).)
* _Bundlers_: Bundlers (like `webpack` or `rollup`) have provided support for a longer period, and they appear in many web-app project-templates. Bundlers scan JS code, examine `import`s, and build aggregated JS files. They have more features -- such as customized path resolution and build-optimizations ("minification", "tree shaking"). However, most (all?) popular bundlers are designed for NodeJS.
(*Question: Is the choice of `import` mechanism all-or-nothing? Or do they interoperate? For example, suppose you use `webpack` to make a base-bundle `$XXX + common widgets` -- and then you load a few extra JS modules in the browser. Will `import`s in the browser find resources from `webpack`'s bundle?*)
### Approaches to linking/loading ES6 for CiviCRM
1. Don't support ES6 modules
* _Pro_: No new code!
* _Con_: You will find it confusing to read documentation+examples in the `$XXX` ecosystem. Authors tend to assume you have figured out ES6. (*If you read `import foo from bar.js`, then you will need to mentally substitute calls like `addScriptFile('bar.js')`.*)
* _Con_: Some libraries may be difficult/impossible to assimilate (*because the code is sprinkled with unresolvable `import`s*).
1. Rely on browser support. All `import`s in Civi-land use physical paths. Rearrange Civi.
* _How_: Rearrange the file-tree (for every Civi-CMS integration) to make physical paths cleaner and more predictable.
* _Pro_: Clean, predictable paths are pleasant.
* _Pro_: Standards-based `import` mechanism. No special tools/libraries.
* _Con_: Major compatibility break with existing CiviCRM deployments/hosts/scripts.
* _Con_: Doesn't support bundling/aggregation.
1. Rely on browser support. All `import`s in Civi-land use physical paths. Sync files during installation.
* _How_: Define a data folder like `[civicrm.files]/js`. Whenever you install/upgrade/remove an extension, synchronize the JS resources to this folder -- and ensure that each file lands in a clean/predictable subdir. The web-browser requests JS files from `[civicrm.files]/js`.
* _Pro_: No major compatibility break.
* _Pro_: Clean, predictable paths are pleasant.
* _Con_: Large file sync's are unpleasant (especially during development).
* _Con_: Doesn't support bundling/aggregation.
1. Rely on browser support. All `import`s in Civi-land use import-maps.
* _How_: In PHP, define a registry of import-paths. Fill this based on the list of installed extensions. Output the `import-map` to the HTML `<HEAD>`.
* _Pro_: Works with existing file-tree.
* _Pro_: Standards-based `import` mechanism. No special tools/libraries.
* _Con_: Firefox+Safari support is... eh... "pending". *There may be a polyfill (not yet assessed).*
* _Con_: Doesn't support bundling/aggregation.
* _Comment_: If we think that Firefox+Safari will have good support in (say) 2023, then we could proceed down this path and simply keep this functionality in "alpha" until 2023.
1. Rely on a bundler. Every extension calls the bundler on its own.
* _How_: In each extension that uses `$XXX` for a UI, define `package.json` (et al) to download a bundler (eg `webpack`) and build a custom bundle. To include resources from another extension, ask Civi for the paths (eg `cv path -x $EXT`). Publish the compiled bundle as part of the extension.
* _Pro_: Use a popular bundler with advanced features (like tree-shaking).
* _Pro_: Each extension developer can choose their own bundler. (Freeedom!!)
* _Con_: All such extensions will have independent versions of `$XXX` (and independent versions of common utilities/widgets/etc).
* _Con_: Upgrades/bugfixes are onerous (*must upgrade every extension; they're all statically-linked*).
* _Con_: If you have a screen (e.g. "The Dashboard") that mixes elements from multiple extensions, then you will get multiple copies of `$XXX`.
* _Con_: Impossible for one extension to add to another extension (*eg CiviDiscount adding elements to CiviEvent*).
* _Con_: Strange bootstrap process (*similar to `civicrm_generated.mysql`; you need a working Civi deployment with the extension enabled before you can write the `*.js` file that will be redistributed.*)
1. Rely on a bundler. Integrate a popular bundler (like `webpack`/`rollup`) into Civi. Run it whenever you install/upgrade/remove extensions.
* _How_: Make a JS script which calls `webpack` (etc). Pass key configuration-data (e.g. the list of bundles and JS files) from Civi to `webpack`.
* _Pro_: Use a popular bundler with advanced features (like tree-shaking).
* _Pro_: Works with existing file-tree.
* _Con_: Requires JS runtime -- eg `node` subprocess, `node` daemon, or browser-based queue-worker.
* _Con_: Vendor-specific `import` mechanism.
* _Con_: No dynamic bundling (*based on user-defined forms, settings, etc*)
1. Rely on a bundler. Use/improve a PHP-based bundler for JS files.
* _How_: There's an alpha-stage JS bundler at https://bitbucket.org/fugu-fuman-library/es6bundler/src/main/. Add it to core. As necessary, contribute to it.
* _Pro_: Works with existing source-tree.
* _Pro_: Supports bundling
* _Pro_: Supports dynamic bundling (*based on user-defined forms, settings, etc*)
* _Con_: It's alpha-stage with no major user-base. It likely needs work.
* _Con_: There is not much of an ecosystem for parsing JS in PHP. You're likely to use approximations (regex) or something slow.
* _Con_: Limited optimizations (Never as good as `webpack`. Ex: You can do minification but not tree-shaking.*)
* _Con_: Vendor-specific `import` mechanism.
1. Rely on 2-step bundler. Use JS to build dependency graph. Use PHP to concatenate final bundle(s).
* _How_: If an extension publishes JS files, then it should also publish a JSON index (listing the files+dependencies). Provide JS devtool to generate the index. In Civi, use the PHP bundler.
* _Pro_: Works with existing source-tree.
* _Pro_: Supports bundling
* _Pro_: Supports dynamic bundling (*based on user-defined forms, settings, etc*)
* _Pro_: JS scanning is done by JS code (*should be able to find a robust JS parser*)
* _Con_: No one else is doing it.
* _Con_: Need to include JS indexer as part of extension-development workflow.
* _Con_: Limited optimizations (Never as good as `webpack`. Ex: You can do minification but not tree-shaking.*)
* _Con_: Vendor-specific `import` mechanism.
## Angular, TypeScript, ng
Angular 14 appears to parallel the original AngularJS in that it has two main file-types:
| Version | Logic/Behavior-Implementation | Layout/Component-Graph |
| -- | -- | -- |
| AngularJS | Javascript (`*.js`) | XHTML (`*.html`) |
| Angular | Typescript (`*.ts`) | XHTML (`*.html`) |
The good news is that the layout/component-graph is broadly the same format - HTML provides the canvas for organizing the choice of components that you want on the screen. This canvas still uses a well-known format which is amenable to reading/filtering/writing with _many_ tools. This gives us a good prognosis for migrating data (*immediately*) and maintaining it (*future updates*).
But... the logic/behavior files changed to Typescript. This requires a TypeScript compiler. Like with linker-loader, the compiler needs to be taught to load classes from different extensions (*ie the type-checker needs full visibility on the list of types - and types can originate in different places*).
I haven't examined of how Angular 14 implements its compiler/linker/loader mechanisms, so the list of approaches here is nowhere near as detailed as the link-loader discussion above. But loosely, the documented/supported mechanisms are built around a CLI tool named `ng` (*implemented with `node`*). The general options appear to be:
* Integrate with `ng`. (This will have similar pro/con as the integrating with the webpack bundler.)
* Examine+decompose the functionality of `ng`. It may be possible to combine this with one of our linker-loader approaches.
* Don't support TypeScript at all. (This will likely be very difficult. It's true that earlier versions of Angular presented JS options, and it's true that Angular compiles down to JS, but contemporary documentation is exclusively geared toward TS. Even if we figure out how, they're liable to change it.)
The adoption of `ng` represents another significant change between AngularJS and contemporary Angular. This is likely very nice for people who write pure-Angular applications -- the `ng` tool provides more functionality and hides more details. Thus, they can make big changes in the mechanics (eg [switching from "Just in Time" compilation to "Ahead of Time" compilation](https://angular.io/guide/aot-compiler)) without significantly changing the DX. But I worry about the breadth of `ng`'s scope -- that your choice is to either (a) do everything "the ng way" or (b) continuously reengineer your process to stay aligned with `ng`.
I have a theory (*but can't fully back it up, as I haven't been following along with Angular 2+ closely*) -- by introducing `ng`, Angular began asserting more ownership over the stack+workflow. This has probably helped pure-Angular development work (because lots of changes can be encapsulated within `ng`) -- but it's has made it harder to integrate with other applications (like Civi and the CMSs). It doesn't seem like a coincidence that AngularJS (*JS library*) had a fairly long/stable 1.x series -- while Angular (*Typescript platform*) has done frequent major-version bumps.
## ReactJS and JSX
(*Sketch/Notes*)
* The graph of ReactJS components is defined as a series of JS functions. These functions can be written in pure JS, but they are typically written in "JSX" (JS with inline HTML).
* From POV of an HTML developer, the extra power of JSX is that it provides a full (Turing complete) language -- where you can mingle function calls, loops, etc.
* From POV of a JS developer, the extra ease of JSX is that embeds HTML (rather than using verbose DOM calls).
* These things are exactly what makes it hard to process in other ways -- eg if the component-graph is defined with JS function-calls, then you're tooling must model "JS function-calls".
* To provide __tooling__ around JSX, you need the ability to compile/parse/generate JSX (aka "encode/decode"; aka "use the abstract syntax tree (AST)").
* Here are some of the things that would require AST/encode-decode capability:
* Drag/drop editor (let a web-user compose the graph of widgets)
* Incremental-upgrades (automatically swap deprecated code with supported code)
* Make client+server logic match-up (use the same document to determine entity-bindings and validations)
* The standard tooling for JSX (compilers/parsers/ASTs) are implemented in JS themselves. But if the server and dev-workflow are in PHP, then you have to either (a) live with constrained callouts between PHP+JS or (b) reimplement JSX tooling in PHP.
* Generally, to support more tooling+workflows, you want a standardized notation such as XML or JSON.
* It is possible to use ReactJS *only* as a runtime (*in the same sense that Civi Profiles (`UFGroup`s) use Quickform as the runtime*).
* You might do this if you want to get some part of ReactJS (e.g. pure-functional code-style; e.g. branding) but don't need "native feel".
* ReactJS without JSX is not the "ReactJS experience". For a developer who's experienced in ReactJS, it will _feel_ alien.
* One example: ["HTM"](https://github.com/developit/htm) is a different templating language that also produces React components. HTM relaxes some of workflow
requirements (e.g. no need for transpiler). However, HTM (specifically) still has similar tooling+AST issues as JSX.
## Walk and/or Chew Gum
Broadly speaking, there is a tension between:
* (a) Providing value to users -- i.e. improving utility/functionality of SearchKit and Form Builder. We have some momentum and regular investment, and we're in the middle of converting existing screens to the newer stack (e.g. `civicrm_admin_ui`). We don't want to kill this.
* (b) Replacing AngularJS -- i.e. migrating to Angular(TS) or ReactJS or similar. This could be as a difficult as a rewrite. If you do a straight-conversion, that's a high-bar -- you may be stuck with a project that cannot be released for 12-18 months. Or you may be stuck with two parallel versions, where the new one never reaches completion.
The challenge is to navigate that tension in a realistic way. It seems unrealistic to stop all improvements for a year. It also seems unrealistic to stay on AngularJS permanently. So how do you address both goals?
To my mind, the most promising concept so far looks roughly like this:
* Take our current data-formats of SK/FB. Formalize them -- define schemas with version-numbers, add enforcement mechanisms (warnings/migrations).
* The data-formats require runtime-interpreters. We currently have one based on AngularJS. We seek to add one based on `$XXX`.
* Gradually phase-out the AngularJS interpreter; gradually phase-in the `$XXX` interpreter. Site-builders and extension-developers use
this data-format throughout the transition.
Here's an example of an incremental transition:
* __Definitions__:
* SKFB-A is the SearchKit/FormBuilder runtime on Angular.
* SKFB-X is the SearchKit/FormBuilder runtime on `$XXX`.
* __Incubation Period__:
* SKFB-A is default. All stable/released functionality is based on SKFB-A.
* SKFB-X is undergoing primary development. You can opt into it (e.g. toggle a flag or extension).
* The format may still undergo small revisions which are implemented/supported by SKFB-A.
* __Sync-Up Period (Soft Freeze)__
* The format is in soft-freeze. Features shouldn't be added (unless doing so helps to achieve the sync-up).
* SKFB-A is default. It may get small bug-fixes.
* SKFB-X is the focus of development attention. The aim is to achieve compatibility with current FMT and most screens.
* __Wind-Down Period__:
* SKFB-X is used for all user-generated screens and many built-in screens.
* SKFB-A is still available - and is used transitionally for screens that require it.
* The format is unfrozen. It may continue to evolve on SKFB-X.
* __Other Considerations__:
* Throughout the process, both SKFB-A and SKFB-X are developed in `civicrm-core.git`.
* Throughout the process, downstream site-builders and developers (of other extensions) may continue to use FMT define screens.https://lab.civicrm.org/dev/core/-/issues/4354Activities created via API should notify Assignees2023-08-03T12:38:52Zwil_SRQActivities created via API should notify AssigneesOverview
----------------------------------------
If the GUI (https://cc.unidosnow.org/civicrm/activity?action=add) would notify assignees when an activity is created. It'd be useful for the API to do so too. The GUI respects the setting...Overview
----------------------------------------
If the GUI (https://cc.unidosnow.org/civicrm/activity?action=add) would notify assignees when an activity is created. It'd be useful for the API to do so too. The GUI respects the setting in Administer > Customize Data and Screens > Display Preferences > Notify Activity Assignees and notification rules by Activity type. It'd be useful for the API to do so too.
Example use-case
----------------------------------------
1. Invoke civicrm_api3('Activity', 'create', []) or civicrm_api4('Activity', 'create', [])
2. Include assignees
Current behaviour
----------------------------------------
The Activity is created but the assignees are not notified, even in situations where creating the equivalent activity via the GUI would have issued notifications.
Proposed behaviour
----------------------------------------
Notify assignees using the same rules and notification format as the GUI.
Comments
----------------------------------------
See https://civicrm.stackexchange.com/q/45078/5446
Workaround is to call CRM_Activity_BAO_Activity::sendToAssignee() separatelyhttps://lab.civicrm.org/dev/core/-/issues/4351Auto Complete Field doesn't exist in FormBuilder2023-08-14T00:32:03ZtreseroAuto Complete Field doesn't exist in FormBuilderIf you have a custom field that is an autocomplete multiple values, formbuilder won't address that. It appears that single selects are the only option for forms.
Here is an example:
I have contacts, and added information is additional c...If you have a custom field that is an autocomplete multiple values, formbuilder won't address that. It appears that single selects are the only option for forms.
Here is an example:
I have contacts, and added information is additional casts traveled in.
In the backend contacts edit, no problem, you can add multiple casts per contact.
![image](/uploads/00c5f87c0bcad9996efeac0134915317/image.png)
But in a form you only can use a select, not multiselect and it doesn't inherit from the base.
![image](/uploads/ef486051db6560a9dbfa98c7c17718bb/image.png)
Therefore, members can't update their casts with multiple casts.https://lab.civicrm.org/dev/core/-/issues/4349Migrate "Edit Profile" popup to SearchKit/FormBuilder, kill BackBone2023-06-09T09:14:03ZcolemanwMigrate "Edit Profile" popup to SearchKit/FormBuilder, kill BackBoneCiviCRM includes an entire javascript framework stack, Backbone + Marionette, and only uses it to do one thing: the "Edit Profile" popup.
It wouldn't be quite the same, but I think we could make something equivalent *enough* using Search...CiviCRM includes an entire javascript framework stack, Backbone + Marionette, and only uses it to do one thing: the "Edit Profile" popup.
It wouldn't be quite the same, but I think we could make something equivalent *enough* using SearchKit and Afform and kill off Backbone once and for all.https://lab.civicrm.org/dev/core/-/issues/4346Find Groups improvements2023-06-09T07:50:00ZyashodhaFind Groups improvementsThe idea is to give more insight into the parents and children of the said group. It's hard to understand the complexity of the group in the current UI esp in the scenarios where the smart groups are heavily used.
I am proposing this c...The idea is to give more insight into the parents and children of the said group. It's hard to understand the complexity of the group in the current UI esp in the scenarios where the smart groups are heavily used.
I am proposing this change which should help significantly by adding these 2 columns to the listing :
- Parent - Add parent column which shows parents count which is a url when clicked lists all parents groups
- Children - Add children column which shows children count which is a url when clicked lists all child groupsyashodhayashodhahttps://lab.civicrm.org/dev/core/-/issues/4344SearchKit: Register Participants for Event is missing from Actions2023-10-21T14:28:40ZlarsssandergreenSearchKit: Register Participants for Event is missing from ActionsUnfortunately, Register Participants for Event isn't available in SK because CRM_Event_Form_Task_Register doesn't work in [standalone mode.](https://docs.civicrm.org/dev/en/latest/searchkit/tasks/#legacy-search-tasks
)
I think this is t...Unfortunately, Register Participants for Event isn't available in SK because CRM_Event_Form_Task_Register doesn't work in [standalone mode.](https://docs.civicrm.org/dev/en/latest/searchkit/tasks/#legacy-search-tasks
)
I think this is the most important missing action by far. Is there a plan on how we transition this to work in SK? I can put some work into it, but don't understand what needs to happen exactly.
From @petednz's question on [chat](https://chat.civicrm.org/civicrm/pl/d66yto1ewp8s5ra5qs94hurk6r), but this is something I've run into a few times.https://lab.civicrm.org/dev/core/-/issues/4342It should be possible to remove a hidden smart group from recipients of a mai...2023-06-28T06:56:18ZlarsssandergreenIt should be possible to remove a hidden smart group from recipients of a mailing that has been copiedIf you create a mailing by copying another mailing that was sent to search results, it is impossible to remove the hidden smart group from the recipients of your new mailing. This can be frustrating, because your intent in copying a mail...If you create a mailing by copying another mailing that was sent to search results, it is impossible to remove the hidden smart group from the recipients of your new mailing. This can be frustrating, because your intent in copying a mailing may not have been to send it to the same contacts, rather to re-use the content. With Mosaico, you can't easily duplicate the content, so this is a pain.
Ideally, hidden smart groups would be mandatory for the original mailing and could be removed for copies. However, that isn't possible because mandatory groups are simply [included groups that are hidden](https://github.com/civicrm/civicrm-core/blob/f0648c892ab4795b9bec873c840e06009ea4800a/ang/crmMailing/BlockRecipients.html#L6).
Options I see:
1. Don't include hidden groups when copying a mailing. Would solve this issue, but then some users are going to expect identical recipients when they copy a mailing because they are copying the mailing in order to resend to the same recipients, changing the content.
1. Add a Copy without recipients option. This seems like it would add UI complexity for something pretty specific.
2. Allow hidden smart groups to be removed from mailings in general, with a confirmation. This feels like the best option to me. Maybe I've started a mailing from search results and later decide I don't want to send it to those search results. Clearly we want to prevent users from removing a hidden group by accident, but as an intentional decision the user confirms, I think this makes sense. Would have to look at how to handle the unsubscribe group in this case.https://lab.civicrm.org/dev/core/-/issues/4341Users without batch permissions may create and run batches2023-06-09T08:53:44ZGavin SmalleyUsers without batch permissions may create and run batchesOverview
----------------------------------------
Users without permission to create batches can not only create batches but also validate and run them.
Reproduction steps
----------------------------------------
1. From a default insta...Overview
----------------------------------------
Users without permission to create batches can not only create batches but also validate and run them.
Reproduction steps
----------------------------------------
1. From a default install of CiviCRM in WordPress access the "WordPress Access Control" page
2. For a given userclass remove all of the following permissions:
- CiviCRM: create manual batch Create an accounting batch (with Access to CiviContribute and View Own/All Manual Batches)
- CiviCRM: edit own manual batches Edit accounting batches created by user
- CiviCRM: edit all manual batches Edit all accounting batches
- CiviCRM: close own manual batches Close accounting batches created by user (with Access to CiviContribute)
- CiviCRM: close all manual batches Close all accounting batches (with Access to CiviContribute)
- CiviCRM: reopen own manual batches Reopen accounting batches created by user (with Access to CiviContribute)
- CiviCRM: reopen all manual batches Reopen all accounting batches (with Access to CiviContribute)
- CiviCRM: view own manual batches View accounting batches created by user (with Access to CiviContribute)
- CiviCRM: view all manual batches View all accounting batches (with Access to CiviContribute)
- CiviCRM: delete own manual batches Delete accounting batches created by user
- CiviCRM: delete all manual batches Delete all accounting batches
- CiviCRM: export own manual batches Export accounting batches created by user
- CiviCRM: export all manual batches Export all accounting batches
3. Create a user for that userclass and login
4. Click "Batch Data Entry" from the Memberships menu
5. Create a batch and press validate.
Current behaviour
----------------------------------------
The batch will run.
Expected behaviour
----------------------------------------
If the user has none of the permissions above they should not be able to create, validate or run a batch.
Environment information
----------------------------------------
* __Browser:__ _Chrome Version 114.0.5735.90 (Official Build) (64-bit)_
* __CiviCRM:__ _5.52.2_
* __CMS:__ _WordPress 6.0.5_
Comments
----------------------------------------
The user can also "save for later" but can never access those saved batches again, as they are (quite correctly) prohibited from seeing the list.https://lab.civicrm.org/dev/core/-/issues/4340Fix behavior of summary action hook2023-06-09T07:48:33ZyashodhaFix behavior of summary action hookFix behavior of summary action hook to make it indeed useful. The hook needs to called for manipulation at the very last. The hook is being called early and then all the sorting etc takes place, rendering it useless.Fix behavior of summary action hook to make it indeed useful. The hook needs to called for manipulation at the very last. The hook is being called early and then all the sorting etc takes place, rendering it useless.yashodhayashodhahttps://lab.civicrm.org/dev/core/-/issues/4339Add hook for member userdashboard for altering results2023-06-09T07:48:14ZyashodhaAdd hook for member userdashboard for altering resultsAdd hook for member userdashboard for altering resultsAdd hook for member userdashboard for altering resultsyashodhayashodhahttps://lab.civicrm.org/dev/core/-/issues/4336Payment on Event Confirmation Page: Does not work when pay later is disabled2023-06-09T18:39:49ZlarsssandergreenPayment on Event Confirmation Page: Does not work when pay later is disabledGreat to have this functionality!
_This has been updated after the fixes in PR 26491._
_There are two cases: 1) with pay later or 2) without pay later._
**If pay later is enabled**, it seems to work as intended, but the pay later inst...Great to have this functionality!
_This has been updated after the fixes in PR 26491._
_There are two cases: 1) with pay later or 2) without pay later._
**If pay later is enabled**, it seems to work as intended, but the pay later instructions are displayed on the payment/confirmation page (they should never be as the user hasn't selected a payment method yet) and on the Thank You page even if they selected another payment method (see screenshots below).
Updated: After submitting, the contribution is created, but it is set to pending (pay later) and no payment is present.
With pay later enabled, it looks like $is_pay_later is always true.
On payment/confirmation:
![image](/uploads/1c406bf9b01bcaa4a430f3d720b5bf7f/image.png)
On Thank You after selecting a payment method that isn't pay later:
![image](/uploads/e2630c45369dbf975ad340c83339b7ec/image.png)
**Without pay later:**
On the payment/confirmation page, something isn't loading right because these billings fields should be filled in and there shouldn't be a card type select:
<img src="/uploads/546c468287908dc976a3e47a73192aed/image.png" width=400>
On submit, we get an error:
` [error] Payment processor exception: Error Unexpected Server Error, please see your logs `
```
[info] $iATS SOAP Response = stdClass Object
(
[ProcessCreditCardResult] => stdClass Object
(
[any] => FailureObject reference not set to an instance of an object.
)
)
```
Fixed:
~~On the first page, with pay later disabled, we get divs that shouldn't be there:~~
<img src="/uploads/cb6e5ac305cc947cb404206fecc5899d/image.png" width=400>https://lab.civicrm.org/dev/core/-/issues/4334Remove Event Info link from Manage Event (and same from Manage Contribution P...2023-06-21T14:55:10ZlarsssandergreenRemove Event Info link from Manage Event (and same from Manage Contribution Page)![image](/uploads/dd822bae24450b2001a604f392198a89/image.png)
I'm not sure this is needed, since it just replicates the link available in the Event Links above, except less completely, as it doesn't include Online Registration, so users...![image](/uploads/dd822bae24450b2001a604f392198a89/image.png)
I'm not sure this is needed, since it just replicates the link available in the Event Links above, except less completely, as it doesn't include Online Registration, so users tend to think this is the only option (some organizations do not use Event Info, preferring to link straight to Registration).
There is a similar thing at the bottom of the first Manage Contribution Page tab that could also be removed, in my opinion. These look kind of weird and aren't really necessary, so I think they can go without losing anything, but am looking for further opinions on this.https://lab.civicrm.org/dev/core/-/issues/4332Error in membership status after a) changing membership type followed by b) p...2023-11-23T07:23:38ZJoeMurrayError in membership status after a) changing membership type followed by b) payment failure## Overview
Using a pay later payment when renewing a membership can lead to problems with the membership status, membership end date and membership type being changed at the time of the renewal being initiated ; these fields are update...## Overview
Using a pay later payment when renewing a membership can lead to problems with the membership status, membership end date and membership type being changed at the time of the renewal being initiated ; these fields are updated without a payment being recorded. It is possible that a payment will never be received, or its processing may fail. It is not easy to revert the data to its former state, or what it would have become through time from date of update to when the correction is attempted. For example, a renewal with a delayed payment might change the status from Grace to Current, the End Date from May 14, 2023 to May 14, 2024, and the Membership Type from General to Student.
These are very old problems dating to at least 2013 I believe.
The problem only occurs when both membership types have the same parent organization, and only for paid memberships. It occurs whether the membership period is Rolling or Fixed, and whether a membership type is being changed or not.
## Proposal
Refactor the current implementation so that a second, temporary membership is created that can store the new information without overwriting the old information until a payment is received for it. The new temporary membership would have a status of Pending. The Pending contribution would be related to the temporary rather than existing membership. When the payment is received (status=complete), the Pending membership's information is used to update the permanent membership, and the temporary membership record is deleted.
## Relevant code
In the Contribution Confirmation page postProcess call [legacyProcessMembership](https://github.com/civicrm/civicrm-core/blob/master/CRM/Contribute/Form/Contribution/Confirm.php#L1623), various fields are updated before the doPayment call is processed [here](https://github.com/civicrm/civicrm-core/blob/master/CRM/Contribute/Form/Contribution/Confirm.php#LL1702C42-L1702C51). As a result, the existing membership record is updated with selected membership type/end date/status after user submits a payment but before the code makes payment request, e.g. to a payment processor. This works if the payment went successfully.
In the case of a payment failure such as for IPN payment processors like Paypal Standard (occasionally when there is a delay on getting IPN callback or if the IPN response is not handled properly like https://lab.civicrm.org/dev/core/-/issues/1931) or for a manual Pay Later payment that isn't received, it leaves the selected membership in current/active state with a changed end date and possibly a different membership type. There is no fallback code written to revert the membership state or set it to Pending, and it isn't easy to reconstruct the data.
## New Behaviour
1. When initiating the Payment for Membership Renewal, create a new membership record and link the contribution in pending status to it. Add a new field, renewing_membership_id, to civicrm_membership to hold a reference from this 'temporary' pending membership to the existing membership that is being renewed. The existing membership record remains unchanged.
2. When the contribution status of the related contribution changes to Complete, update the original membership with the information from the temporary membership and delete the temporary membership.
Recommendation: delay the creation of activities for Membership Renewal (id=8), Change Membership Status (id=35) and Change Membership Type (id=36) until the contribution is completed.
Recommendation: create a new Activity Type, Membership Renewal Pending, to be created when the renewal request is received. In its body, provide: "ID of Membership being renewed: xx, Number of Periods: yy, Membership Type: [Label of Membership Type".
## Implementation:
1. Modify [here](https://github.com/civicrm/civicrm-core/blob/master/CRM/Contribute/Form/Contribution/Confirm.php#L2900), CRM_Member_BAO_Membership::getContactMembership and [here](https://github.com/civicrm/civicrm-core/blob/master/CRM/Contribute/Form/Contribution/Confirm.php#L2939).
2. Add / update new / existing unit tests on these new scenarios.EdselopezEdselopezhttps://lab.civicrm.org/dev/core/-/issues/4328Double opt-in requires traditional bounce handling to be enabled2023-06-28T06:55:29ZJonGoldDouble opt-in requires traditional bounce handling to be enabledOverview
----------------------------------------
The default double opt-in email says:
> You have a pending subscription to the {subscribe.group} mailing list. To confirm this subscription, reply to this email or click <a href="{subscri...Overview
----------------------------------------
The default double opt-in email says:
> You have a pending subscription to the {subscribe.group} mailing list. To confirm this subscription, reply to this email or click <a href="{subscribe.url}">here</a>.
However, "reply to this email" requires traditional bounce handling (with a bounce mailbox) to be set up. In this day and age that's less common compared to using a third party mailer that sends bounce notifications via API (to [Airmail](https://civicrm.org/extensions/airmail) or one of its many cousins).
Moreover, it's a usability issue to put "here" as the clickable text. It should be more like: "Please click here to <a href="{subscribe.url}">confirm your subscription.</a>.
I want to get concept approval - and also guidance on whether we should update existing templates or just new installs. Personally I think we should update any non-customized template.