Group opt in and subscribe/unsubscribe in form builder
Group subscribe and unsubscribe in form builder
This issue is an attempt to add functionality to handle group subscribe and unsubscribe in form builder as per the roadmap #3761.
Scenarios
Here are a few scenarios that we'd like to cater for...
Opt in only flow
This flow is useful to give people the option to opt in to a group as part of another flow, e.g. when I register for an event, I might want to opt into an events newsletter. Note: I don't typically want to opt out of such a newsletter in these situations, and an opt out is likey unexpected and undesirable.
Opt in flows are also useful to create newsletter sign ups that contain multiple groups/mailing lists that people can sign up for.
We render a checkbox and group name. When the checkbox is ticked, we add a contact to the group (this might be a no-op if they are already in the group). If the checkbox is not ticked, we do nothing (i.e. we do not unsubscribe anyone that is already subscribed to the group).
We render a separate component for each group that the contact wants to opt into (rather than a single component that contains all groups).
By default checkboxes are not ticked when the form is loaded. This aligns nicely for workflows (e.g. GDPR compliant workflows) where opting in should be a conscious decision. However, it should be possible to set the checkbox to be ticked by default if desired. This may be appropriate in certain situations, e.g. it would be reasonable to pre-check a 'Please send me members newsletter' checkbox in a membership form if you wanted most people to receive the members newsletter but also wanted to give the option to opt out.
Subscribe and unsubscribe flow
This allows people to both add themselves and remove themselves from groups.
This is typically used in 'Manage my notifications/communication preferences' screens and similar. It allows people to see what groups they are currently subscribed to, and subscribe and unsubscribe from them as appropriate.
We render a separate UI element for each group that the contact wants to opt into. When the checkbox is ticked, we add a contact to the group (if they are not already in it). If the checkbox is not ticked, we remove them from the group (if they are already in it).
Automatic subscribe / unsubscribe flow
Sometimes we might want to automatically sign someone up to a mailing list without presenting a checkbox to tick. For example...
One solution in this situation would be to add a hidden and checked subscribe or opt in element. In the situation that submitting the form should unsubscribe you, adding an unchecked subscribe/unsubscribe component should do the trick.
The data model and form processing
I'm not 100% sure what the mark up for this should look like. An email join looks like this:
<fieldset af-fieldset="Individual1" class="af-container" af-title="Individual 1">
<afblock-name-individual></afblock-name-individual>
<div af-join="Email">
<af-field name="email" />
</div>
</fieldset>
But group subscriptions and un-subscriptions don't work quite the same way.
In terms of the API, I think that what we want to do can probably be achieved with something like the save api:
$results = \Civi\Api4\GroupContact::save()
->addRecord([
'group_id' => 2,
'contact_id' => 203,
'status' => 'Added',
])
->setMatch([
'group_id',
'contact_id',
])
->execute();
But I think there are a few tweaks that we'd want to do, e.g. we ideally do not want to record a status=removed when submitting a form with an unsubscribe/subscribe if they were not in the group in the first place.
And I am not sure how the save api gets called in formbuilder.
In terms of what the html might look like, I am wondering if it makes sense to create some components that encapsulate some of the logic, so that it ends up looking like this.
<fieldset af-fieldset="Individual1" class="af-container" af-title="Individual 1">
<afblock-name-individual></afblock-name-individual>
<af-group group_id="2" />
</fieldset>
It might make sense to add a mode property that could be set to control the display and behaviour, e.g. <af-group group_id="2" mode='opt-in'/>
- normal - opts in and out
- opt-in - only allows opting in
- auto-add - hidden and adds people on submission
- auto-remove - hidden and removes people on submission
And a default property for opt in workflows, e.g.
The above component would be responsible for generating the checkbox and text and for generating appropriate values when the form is submitted.
I can't see a way of doing it with the existing components. I might be missing something conceptual but here is my attempt. It made me think that a new component would probably make things easier.
<fieldset af-fieldset="Individual1" class="af-container" af-title="Individual 1">
<afblock-name-individual></afblock-name-individual>
<div af-join="GroupContact">
<af-field name="group_id" /> <?-- do we also need to join on group to get the name? -->
<af-field name="status" /> <?-- Would need to translate a checkbox to value?'Added':'Removed'? -->
</div>
</fieldset>
Email confirmation
At the moment, double opt in is closely linked to subscribing to an email group. IIRC in the past we have used something like
<?php
civicrm_api3('MailingEventSubscribe', 'create', ...);
But this has a few drawbacks, including having to confirm multiple email subscriptions for a single form. Since we are also working on #4232 (closed), we think it probably makes sense to leverage that for any logged out forms which will require that an email needs to be confirmed before any form processing occurs.