diff --git a/api/cora/setting.example.php b/api/cora/setting.example.php index cd89d72..b898295 100644 --- a/api/cora/setting.example.php +++ b/api/cora/setting.example.php @@ -111,19 +111,26 @@ final class setting { 'beestat_root_uri' => '', /** - * Your Mailchimp API Key; provided to you when you create a Mailchimp - * developer account. + * Your Mailgun API Key. * - * Example: hcU74TJgGS5k7vuw3NSzkRMSWNPkv8Af-us18 + * Example: 4b34e48e768fa45c4a6ac65dd4cf1da9-7e28d3c3-61713777 */ - 'mailchimp_api_key' => '', + 'mailgun_api_key' => '', /** - * ID of the mailing list to send emails to. + * API base URL including the sending domain. Make sure to include the + * trailing slash. * - * Example: uw3NSzkRMS + * Example: https://api.mailgun.net/v3/ */ - 'mailchimp_list_id' => '', + 'mailgun_base_url' => '', + + /** + * The specific newsletter to subscribe users to. + * + * Example: newsletter@app.beestat.io + */ + 'mailgun_newsletter' => '', /** * Auth ID for Smarty Streets address verification. diff --git a/api/ecobee.php b/api/ecobee.php index 53fcb87..c757838 100644 --- a/api/ecobee.php +++ b/api/ecobee.php @@ -60,13 +60,9 @@ class ecobee extends external_api { ); $identifiers = []; - $email_addresses = []; foreach($response['thermostatList'] as $thermostat) { $runtime = $thermostat['runtime']; $identifiers[] = $thermostat['identifier']; - - $notification_settings = $thermostat['notificationSettings']; - $email_addresses = array_merge($email_addresses, $notification_settings['emailAddresses']); } // Look to see if any of the returned thermostats exist. This does not use @@ -119,22 +115,6 @@ class ecobee extends external_api { else { $this->api('user', 'create_anonymous_user'); $this->api('ecobee_token', 'create', ['attributes' => $ecobee_token]); - - if(count($email_addresses) > 0) { - try { - $this->api( - 'mailchimp', - 'subscribe', - [ - 'email_address' => $email_addresses[0] - ] - ); - } catch(Exception $e) { - // Ignore failed subscribe exceptions since it's not critical to the - // success of this. Everything is logged regardless. - } - } - } // Redirect to the proper location. diff --git a/api/ecobee_thermostat.php b/api/ecobee_thermostat.php index 38e5bae..5c8ebe6 100644 --- a/api/ecobee_thermostat.php +++ b/api/ecobee_thermostat.php @@ -99,6 +99,7 @@ class ecobee_thermostat extends cora\crud { // Loop over the returned thermostats and create/update them as necessary. $thermostat_ids_to_keep = []; + $email_addresses = []; foreach($response['thermostatList'] as $api_thermostat) { $ecobee_thermostat = $this->get( [ @@ -169,6 +170,12 @@ class ecobee_thermostat extends cora\crud { ] ); + foreach($api_thermostat['notificationSettings']['emailAddresses'] as $email_address) { + if(preg_match('/.+@.+\..+/', $email_address) === 1) { + $email_addresses[] = trim(strtolower($email_address)); + } + } + // Grab a bunch of attributes from the ecobee_thermostat and attach them // to the thermostat. $attributes = []; @@ -260,6 +267,19 @@ class ecobee_thermostat extends cora\crud { ); } + // Update the email_address on the user. + if(count($email_addresses) > 0) { + $email_address_counts = array_count_values($email_addresses); + arsort($email_address_counts); + $email_address = array_keys(array_slice($email_address_counts, 0, 1, true))[0]; + $this->api('user', 'update', [ + 'attributes' => [ + 'user_id' => $this->session->get_user_id(), + 'email_address' => $email_address + ] + ]); + } + // Inactivate any ecobee_thermostats that were no longer returned. $thermostats = $this->api('thermostat', 'read'); $ecobee_thermostat_ids_to_return = []; diff --git a/api/external_api.php b/api/external_api.php index 748d0c3..6b1a2c9 100644 --- a/api/external_api.php +++ b/api/external_api.php @@ -1,7 +1,7 @@ curl_info = curl_getinfo($curl_handle); - if($curl_response === false || curl_errno($curl_handle) !== 0) { + if( + $curl_response === false || + curl_errno($curl_handle) !== 0 + ) { // Error logging if($this::$log_mysql === 'all' || $this::$log_mysql === 'error') { $this->log_mysql($curl_response, true); @@ -103,7 +106,6 @@ class external_api extends cora\api { 'curl_error' => curl_error($curl_handle) ] ); - } // General (success) logging diff --git a/api/mailchimp.php b/api/mailchimp.php deleted file mode 100644 index dd44dfa..0000000 --- a/api/mailchimp.php +++ /dev/null @@ -1,80 +0,0 @@ -curl([ - 'url' => 'https://us18.api.mailchimp.com/3.0/' . $endpoint, - 'post_fields' => json_encode($data, JSON_FORCE_OBJECT), - 'method' => $method, - 'header' => [ - 'Authorization: Basic ' . base64_encode(':' . $this->setting->get('mailchimp_api_key')), - 'Content-Type: application/x-www-form-urlencoded' - ] - ]); - - $response = json_decode($curl_response, true); - - if ($response === null) { - throw new Exception('Invalid JSON'); - } - - return $response; - } - - /** - * Subscribe an email address to the mailing list. This will only mark you - * as "pending" so you have to click a link in the email to actually - * subscribe. - * - * @param string $email_address The email address to subscribe. - * - * @throws Exception If subscribing to the mailing list fails for some - * reason. For example, if already subscribed. - * - * @return array The MailChimp response. - */ - public function subscribe($email_address) { - $method = 'POST'; - - $endpoint = - 'lists/' . - $this->setting->get('mailchimp_list_id') . - '/members/' - ; - - $data = [ - 'email_address' => $email_address, - 'status' => 'pending' - ]; - - $response = $this->mailchimp_api($method, $endpoint, $data); - - if(isset($response['id']) === false) { - throw new Exception('Could not subscribe to mailing list.'); - } - - return $response; - } -} diff --git a/api/mailgun.php b/api/mailgun.php new file mode 100644 index 0000000..768a4ce --- /dev/null +++ b/api/mailgun.php @@ -0,0 +1,123 @@ + [], + 'public' => [ + 'subscribe', + 'unsubscribe' + ] + ]; + + /** + * Send an API call off to mailgun + * + * @param string $method HTTP Method. + * @param string $endpoint API Endpoint. + * @param array $data API request data. + * + * @throws Exception If mailgun did not return valid JSON. + * + * @return array The mailgun response. + */ + private function mailgun_api($method, $endpoint, $data) { + $curl_response = $this->curl([ + 'url' => $this->setting->get('mailgun_base_url') . $endpoint, + 'post_fields' => $data, + 'method' => $method, + 'header' => [ + 'Authorization: Basic ' . base64_encode('api:' . $this->setting->get('mailgun_api_key')), + 'Content-Type: multipart/form-data' + ] + ]); + + $response = json_decode($curl_response, true); + + if ($response === null) { + throw new cora\exception('Invalid JSON', 10600); + } + + return $response; + } + + /** + * Subscribe to the mailing list. + * + * @param string $email_address The email address to subscribe. + * + * @throws exception If the subscribe failed. + * + * @return array Subscriber info. + */ + public function subscribe($email_address) { + $method = 'POST'; + + $endpoint = 'lists/' . $this->setting->get('mailgun_newsletter') . '/members'; + + $data = [ + 'address' => $email_address, + 'subscribed' => 'yes', + 'upsert' => 'yes' + ]; + + $response = $this->mailgun_api($method, $endpoint, $data); + + if ( + isset($response['member']) && + isset($response['member']['address']) && + isset($response['member']['subscribed']) && + $response['member']['address'] === $email_address && + $response['member']['subscribed'] === true + ) { + return $response['member']; + } else { + throw new cora\exception('Failed to subscribe.', 10601); + } + } + + /** + * Unsubscribe from the mailing list. + * + * @param string $email_address The email address to unsubscribe. + * + * @throws exception If the unsubscribe failed. + * + * @return array Subscriber info. + */ + public function unsubscribe($email_address) { + $method = 'POST'; + + $endpoint = 'lists/' . $this->setting->get('mailgun_newsletter') . '/members'; + + $data = [ + 'address' => $email_address, + 'subscribed' => 'no', + 'upsert' => 'yes' + ]; + + $response = $this->mailgun_api($method, $endpoint, $data); + + if ( + isset($response['member']) && + isset($response['member']['address']) && + isset($response['member']['subscribed']) && + $response['member']['address'] === $email_address && + $response['member']['subscribed'] === false + ) { + return $response['member']; + } else { + throw new cora\exception('Failed to unsubscribe.', 10602); + } + } +} diff --git a/api/mailchimp_api_cache.php b/api/mailgun_api_cache.php similarity index 55% rename from api/mailchimp_api_cache.php rename to api/mailgun_api_cache.php index 35f5d28..7d7ac7f 100644 --- a/api/mailchimp_api_cache.php +++ b/api/mailgun_api_cache.php @@ -1,8 +1,8 @@ -get('environment') === 'dev' || $setting->get('environment') === 'd echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; + echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; diff --git a/js/layer/load.js b/js/layer/load.js index 5f695f0..2986ba5 100644 --- a/js/layer/load.js +++ b/js/layer/load.js @@ -225,7 +225,14 @@ beestat.layer.load.prototype.decorate_ = function(parent) { } } - if ( + /* + * Show the first run modal or the announcements modal if there are unread + * important announcements. + */ + if (beestat.setting('first_run') === true) { + beestat.setting('first_run', false); + (new beestat.component.modal.newsletter()).render(); + } else if ( last_read_announcement_id === undefined || ( most_recent_important_announcement_id !== undefined && @@ -234,7 +241,6 @@ beestat.layer.load.prototype.decorate_ = function(parent) { ) { (new beestat.component.modal.announcements()).render(); } - }); api.send();