Add preconfirm_subscriptions=true/false
new subs API.
Sending th optional flag as `trunue` in the POST /api/subscrirs body will skip sending opt-iconfirmation e-mails to subscribers and mark list subscriptions in the request a`confirmed`.
This commit is contained in:
parent
708d0e0b00
commit
ad0a0e0841
6 changed files with 57 additions and 12 deletions
|
@ -643,7 +643,14 @@ func insertSubscriber(req subimporter.SubReq, app *App) (models.Subscriber, bool
|
|||
}
|
||||
req.UUID = uu.String()
|
||||
|
||||
isNew := true
|
||||
var (
|
||||
isNew = true
|
||||
subStatus = models.SubscriptionStatusUnconfirmed
|
||||
)
|
||||
if req.PreconfirmSubs {
|
||||
subStatus = models.SubscriptionStatusConfirmed
|
||||
}
|
||||
|
||||
if err = app.queries.InsertSubscriber.Get(&req.ID,
|
||||
req.UUID,
|
||||
req.Email,
|
||||
|
@ -651,7 +658,8 @@ func insertSubscriber(req subimporter.SubReq, app *App) (models.Subscriber, bool
|
|||
req.Status,
|
||||
req.Attribs,
|
||||
req.Lists,
|
||||
req.ListUUIDs); err != nil {
|
||||
req.ListUUIDs,
|
||||
subStatus); err != nil {
|
||||
if pqErr, ok := err.(*pq.Error); ok && pqErr.Constraint == "subscribers_email_key" {
|
||||
isNew = false
|
||||
} else {
|
||||
|
@ -670,9 +678,13 @@ func insertSubscriber(req subimporter.SubReq, app *App) (models.Subscriber, bool
|
|||
return sub, false, false, err
|
||||
}
|
||||
|
||||
// Send a confirmation e-mail (if there are any double opt-in lists).
|
||||
num, _ := sendOptinConfirmation(sub, []int64(req.Lists), app)
|
||||
return sub, isNew, num > 0, nil
|
||||
hasOptin := false
|
||||
if !req.PreconfirmSubs {
|
||||
// Send a confirmation e-mail (if there are any double opt-in lists).
|
||||
num, _ := sendOptinConfirmation(sub, []int64(req.Lists), app)
|
||||
hasOptin = num > 0
|
||||
}
|
||||
return sub, isNew, hasOptin, nil
|
||||
}
|
||||
|
||||
// getSubscriber gets a single subscriber by ID, uuid, or e-mail in that order.
|
||||
|
|
|
@ -33,4 +33,36 @@ describe('Forms', () => {
|
|||
cy.get('ul[data-cy=lists] .checkbox').click();
|
||||
cy.get('[data-cy=form] pre').should('not.exist');
|
||||
});
|
||||
|
||||
it('Subscribes from public form page', () => {
|
||||
// Create a public test list.
|
||||
cy.request('POST', '/api/lists', { name: 'test-list', type: 'public', optin: 'single' });
|
||||
|
||||
// Open the public page and subscribe to alternating lists multiple times.
|
||||
// There should be no errors and two new subscribers should be subscribed to two lists.
|
||||
for (let i = 0; i < 2; i++) {
|
||||
for (let j = 0; j < 2; j++) {
|
||||
cy.loginAndVisit('/subscription/form');
|
||||
cy.get('input[name=email]').clear().type(`test${i}@test.com`);
|
||||
cy.get('input[name=name]').clear().type(`test${i}`);
|
||||
cy.get('input[type=checkbox]').eq(j).click();
|
||||
cy.get('button').click();
|
||||
cy.wait(250);
|
||||
cy.get('.wrap').contains(/has been sent|successfully/);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify form subscriptions.
|
||||
cy.request('/api/subscribers').should((response) => {
|
||||
const { data } = response.body;
|
||||
|
||||
// Two new + two dummy subscribers that are there by default.
|
||||
expect(data.total).to.equal(4);
|
||||
|
||||
// The two new subscribers should each have two list subscriptions.
|
||||
for (let i = 0; i < 2; i++) {
|
||||
expect(data.results.find((s) => s.email === `test${i}@test.com`).lists.length).to.equal(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -60,7 +60,7 @@ describe('Subscribers', () => {
|
|||
|
||||
|
||||
cases.forEach((c, n) => {
|
||||
// Select one of the 2 subscriber in the table.
|
||||
// Select one of the 2 subscribers in the table.
|
||||
Object.keys(c.rows).forEach((r) => {
|
||||
cy.get('tbody td.checkbox-cell .checkbox').eq(r).click();
|
||||
});
|
||||
|
@ -86,7 +86,7 @@ describe('Subscribers', () => {
|
|||
cy.wrap($el).find('.tag').should('have.length', c.rows[r].length);
|
||||
c.rows[r].forEach((status, n) => {
|
||||
// eg: .tag(n).unconfirmed
|
||||
cy.wrap($el).find(`.tag:nth-child(${n + 1}).${status}`);
|
||||
cy.wrap($el).find('.tag').eq(n).should('have.class', status);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -133,6 +133,7 @@ describe('Subscribers', () => {
|
|||
});
|
||||
|
||||
// Confirm the edits on the table.
|
||||
cy.wait(250);
|
||||
cy.get('tbody tr').each(($el) => {
|
||||
cy.wrap($el).find('td[data-id]').invoke('attr', 'data-id').then((id) => {
|
||||
cy.wrap($el).find('td[data-label=E-mail]').contains(rows[id].email);
|
||||
|
@ -140,7 +141,6 @@ describe('Subscribers', () => {
|
|||
cy.wrap($el).find('td[data-label=Status]').contains(rows[id].status, { matchCase: false });
|
||||
|
||||
// Both lists on the enabled sub should be 'unconfirmed' and the blocklisted one, 'unsubscribed.'
|
||||
cy.wait(250);
|
||||
cy.wrap($el).find(`.tags .${rows[id].status === 'enabled' ? 'unconfirmed' : 'unsubscribed'}`)
|
||||
.its('length').should('eq', 2);
|
||||
cy.wrap($el).find('td[data-label=Lists]').then((l) => {
|
||||
|
|
|
@ -89,8 +89,9 @@ type Status struct {
|
|||
// SubReq is a wrapper over the Subscriber model.
|
||||
type SubReq struct {
|
||||
models.Subscriber
|
||||
Lists pq.Int64Array `json:"lists"`
|
||||
ListUUIDs pq.StringArray `json:"list_uuids"`
|
||||
Lists pq.Int64Array `json:"lists"`
|
||||
ListUUIDs pq.StringArray `json:"list_uuids"`
|
||||
PreconfirmSubs bool `json:"preconfirm_subscriptions"`
|
||||
}
|
||||
|
||||
type importStatusTpl struct {
|
||||
|
|
|
@ -71,7 +71,7 @@ subs AS (
|
|||
VALUES(
|
||||
(SELECT id FROM sub),
|
||||
UNNEST(ARRAY(SELECT id FROM listIDs)),
|
||||
(CASE WHEN $4='blocklisted' THEN 'unsubscribed'::subscription_status ELSE 'unconfirmed' END)
|
||||
(CASE WHEN $4='blocklisted' THEN 'unsubscribed'::subscription_status ELSE $8::subscription_status END)
|
||||
)
|
||||
ON CONFLICT (subscriber_id, list_id) DO UPDATE
|
||||
SET updated_at=NOW()
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<div>
|
||||
<p>
|
||||
<label>{{ L.T "subscribers.email" }}</label>
|
||||
<input name="email" required="true" type="email" placeholder="{{ L.T "subscribers.email" }}" >
|
||||
<input name="email" required="true" type="email" placeholder="{{ L.T "subscribers.email" }}" autofocus="true" >
|
||||
</p>
|
||||
<p>
|
||||
<label>{{ L.T "public.subName" }}</label>
|
||||
|
|
Loading…
Reference in a new issue