alfio.plugin.mailchimp.MailChimpPlugin.java Source code

Java tutorial

Introduction

Here is the source code for alfio.plugin.mailchimp.MailChimpPlugin.java

Source

/**
 * This file is part of alf.io.
 *
 * alf.io is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * alf.io is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with alf.io.  If not, see <http://www.gnu.org/licenses/>.
 */
package alfio.plugin.mailchimp;

import alfio.model.*;
import alfio.model.plugin.PluginConfigOption;
import alfio.model.system.ComponentType;
import alfio.plugin.PluginDataStorageProvider;
import alfio.plugin.PluginDataStorageProvider.PluginDataStorage;
import alfio.plugin.ReservationConfirmationPlugin;
import alfio.plugin.TicketAssignmentPlugin;
import alfio.plugin.WaitingQueueSubscriptionPlugin;
import alfio.util.Json;
import lombok.extern.log4j.Log4j2;
import okhttp3.*;
import org.apache.commons.codec.binary.Hex;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

@Log4j2
public class MailChimpPlugin
        implements ReservationConfirmationPlugin, TicketAssignmentPlugin, WaitingQueueSubscriptionPlugin {

    private static final String DATA_CENTER = "dataCenter";
    private static final String API_KEY = "apiKey";
    private static final String LIST_ID = "listId";
    private static final String LIST_ADDRESS = "https://%s.api.mailchimp.com/3.0/lists/%s/";
    private static final String LIST_MEMBERS = "members/";
    private static final String MERGE_FIELDS = "merge-fields/";
    private static final String FAILURE_MSG = "cannot add user {email: %s, name:%s, language: %s} to the list (%s)";
    private static final String ALFIO_EVENT_KEY = "ALFIO_EKEY";
    private static final String APPLICATION_JSON = "application/json";
    private final String id = "alfio.mailchimp";
    private final PluginDataStorage pluginDataStorage;
    private final OkHttpClient httpClient = new OkHttpClient();

    public MailChimpPlugin(PluginDataStorageProvider pluginDataStorageProvider) {
        this.pluginDataStorage = pluginDataStorageProvider.getDataStorage(id);
    }

    @Override
    public void onTicketAssignment(Ticket ticket) {

        Event event = pluginDataStorage.getEventById(ticket.getEventId());
        CustomerName customerName = new CustomerName(ticket.getFullName(), ticket.getFirstName(),
                ticket.getLastName(), event);
        subscribeUser(ticket.getEmail(), customerName, ticket.getUserLanguage(), ticket.getEventId(),
                event.getShortName());
    }

    @Override
    public void onReservationConfirmation(TicketReservation ticketReservation, int eventId) {
        Event event = pluginDataStorage.getEventById(eventId);
        CustomerName customerName = new CustomerName(ticketReservation.getFullName(),
                ticketReservation.getFirstName(), ticketReservation.getLastName(), event);
        subscribeUser(ticketReservation.getEmail(), customerName, ticketReservation.getUserLanguage(), eventId,
                event.getShortName());
    }

    @Override
    public void onWaitingQueueSubscription(WaitingQueueSubscription waitingQueueSubscription) {
        Event event = pluginDataStorage.getEventById(waitingQueueSubscription.getEventId());
        CustomerName customerName = new CustomerName(waitingQueueSubscription.getFullName(),
                waitingQueueSubscription.getFirstName(), waitingQueueSubscription.getLastName(), event);
        subscribeUser(waitingQueueSubscription.getEmailAddress(), customerName,
                waitingQueueSubscription.getUserLanguage(), waitingQueueSubscription.getEventId(),
                event.getShortName());
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public String getName() {
        return "Mailchimp newsletter subscriber";
    }

    @Override
    public boolean isEnabled(int eventId) {
        return pluginDataStorage.getConfigValue(ENABLED_CONF_NAME, eventId).map(Boolean::parseBoolean)
                .orElse(false);
    }

    @Override
    public Collection<PluginConfigOption> getConfigOptions(int eventId) {
        return Arrays.asList(
                new PluginConfigOption(getId(), eventId, DATA_CENTER, "",
                        "The MailChimp data center used by your account (e.g. us6)", ComponentType.TEXT),
                new PluginConfigOption(getId(), eventId, API_KEY, "", "the Mailchimp API Key", ComponentType.TEXT),
                new PluginConfigOption(getId(), eventId, LIST_ID, "",
                        "the list ID, see http://kb.mailchimp.com/lists/manage-contacts/find-your-list-id",
                        ComponentType.TEXT));
    }

    @Override
    public void install(int eventId) {
        getConfigOptions(eventId).forEach(o -> pluginDataStorage.insertConfigValue(eventId, o.getOptionName(),
                o.getOptionValue(), o.getDescription(), o.getComponentType()));
    }

    private Optional<String> getListAddress(int eventId, String email, CustomerName name, String language) {
        Optional<String> dataCenter = pluginDataStorage.getConfigValue(DATA_CENTER, eventId);
        Optional<String> listId = pluginDataStorage.getConfigValue(LIST_ID, eventId);
        if (dataCenter.isPresent() && listId.isPresent()) {
            return Optional.of(String.format(LIST_ADDRESS, dataCenter.get(), listId.get()));
        } else {
            pluginDataStorage.registerFailure(
                    String.format(FAILURE_MSG, email, name, language, "check listId and dataCenter"), eventId);
        }
        return Optional.empty();
    }

    private Optional<String> getApiKey(int eventId, String email, CustomerName name, String language) {
        Optional<String> apiKey = pluginDataStorage.getConfigValue(API_KEY, eventId);
        if (!apiKey.isPresent()) {
            pluginDataStorage.registerFailure(String.format(FAILURE_MSG, email, name, language, "missing API Key"),
                    eventId);
        }
        return apiKey;
    }

    private void subscribeUser(String email, CustomerName name, String language, int eventId, String eventKey) {
        Optional<String> listAddress = getListAddress(eventId, email, name, language);
        Optional<String> apiKey = getApiKey(eventId, email, name, language);
        if (listAddress.isPresent() && apiKey.isPresent()) {
            createMergeFieldIfNotPresent(listAddress.get(), apiKey.get(), eventId, eventKey);
            send(eventId, listAddress.get() + LIST_MEMBERS + getMd5Email(email), apiKey.get(), email, name,
                    language, eventKey);
        }
    }

    static String getMd5Email(String email) {
        try {
            return Hex.encodeHexString(MessageDigest.getInstance("MD5").digest(email.trim().getBytes("UTF-8")));
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            return email;//should never happen...
        }
    }

    private boolean send(int eventId, String address, String apiKey, String email, CustomerName name,
            String language, String eventKey) {
        Map<String, Object> content = new HashMap<>();
        content.put("email_address", email);
        content.put("status", "subscribed");
        Map<String, String> mergeFields = new HashMap<>();
        mergeFields.put("FNAME", name.isHasFirstAndLastName() ? name.getFirstName() : name.getFullName());
        mergeFields.put(ALFIO_EVENT_KEY, eventKey);
        content.put("merge_fields", mergeFields);
        content.put("language", language);
        Request request = new Request.Builder().url(address)
                .header("Authorization", Credentials.basic("alfio", apiKey))
                .put(RequestBody.create(MediaType.parse(APPLICATION_JSON), Json.GSON.toJson(content, Map.class)))
                .build();
        try (Response response = httpClient.newCall(request).execute()) {

            if (response.isSuccessful()) {
                pluginDataStorage.registerSuccess(String.format("user %s has been subscribed to list", email),
                        eventId);
                return true;
            }
            ResponseBody body = response.body();
            if (body == null) {
                return false;
            }
            String responseBody = body.string();
            if (response.code() != 400 || responseBody.contains("\"errors\"")) {
                pluginDataStorage.registerFailure(String.format(FAILURE_MSG, email, name, language, responseBody),
                        eventId);
                return false;
            } else {
                pluginDataStorage.registerWarning(String.format(FAILURE_MSG, email, name, language, responseBody),
                        eventId);
            }
            return true;
        } catch (IOException e) {
            pluginDataStorage.registerFailure(String.format(FAILURE_MSG, email, name, language, e.toString()),
                    eventId);
            return false;
        }
    }

    private void createMergeFieldIfNotPresent(String listAddress, String apiKey, int eventId, String eventKey) {
        Request request = new Request.Builder().url(listAddress + MERGE_FIELDS)
                .header("Authorization", Credentials.basic("alfio", apiKey)).get().build();
        try (Response response = httpClient.newCall(request).execute()) {
            ResponseBody body = response.body();
            if (body == null) {
                log.warn("null response from mailchimp for list {}", listAddress);
                return;
            }
            String responseBody = body.string();
            if (!responseBody.contains(ALFIO_EVENT_KEY)) {
                log.debug("can't find ALFIO_EKEY for event " + eventKey);
                createMergeField(listAddress, apiKey, eventKey, eventId);
            }
        } catch (IOException e) {
            pluginDataStorage.registerFailure(
                    String.format("Cannot get merge fields for %s, got: %s", eventKey, e.getMessage()), eventId);
            log.warn("exception while reading merge fields for event id " + eventId, e);
        }
    }

    private void createMergeField(String listAddress, String apiKey, String eventKey, int eventId) {

        Map<String, Object> mergeField = new HashMap<>();
        mergeField.put("tag", ALFIO_EVENT_KEY);
        mergeField.put("name", "Alfio's event key");
        mergeField.put("type", "text");
        mergeField.put("required", false);
        mergeField.put("public", false);

        Request request = new Request.Builder().url(listAddress + MERGE_FIELDS)
                .header("Authorization", Credentials.basic("alfio", apiKey)).post(RequestBody
                        .create(MediaType.parse(APPLICATION_JSON), Json.GSON.toJson(mergeField, Map.class)))
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                ResponseBody body = response.body();
                log.debug("can't create {} merge field. Got: {}", ALFIO_EVENT_KEY,
                        body != null ? body.string() : "null");
            }
        } catch (IOException e) {
            pluginDataStorage.registerFailure(
                    String.format("Cannot create merge field for %s, got: %s", eventKey, e.getMessage()), eventId);
            log.warn("exception while creating ALFIO_EKEY for event id " + eventId, e);
        }
    }

}