PluginProbe ʕ •ᴥ•ʔ
ElementsKit Elementor Addons – Advanced Widgets & Templates Addons for Elementor / 4.0.2
ElementsKit Elementor Addons – Advanced Widgets & Templates Addons for Elementor v4.0.2
4.0.2 4.0.1 4.0.0 3.10.02 3.10.01 3.9.10 3.9.9 3.9.8 3.9.7 3.9.5 3.9.6 3.9.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.3.1 2.3.1.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.4.0 2.5.0 2.5.1 2.5.10 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.5 2.8.6 2.8.7 2.8.8 2.9.0 2.9.1 2.9.2 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 3.4.9 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.6.0 3.6.1 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8.0 3.8.1 3.8.2 3.9.0 3.9.1 3.9.2 trunk 1.2.6 1.2.7 1.2.9 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.2 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.0.9.1 2.0.9.2 2.0.9.3
elementskit-lite / widgets / mail-chimp / mail-chimp-api.php
elementskit-lite / widgets / mail-chimp Last commit date
mail-chimp-api.php 1 week ago mail-chimp-handler.php 1 week ago mail-chimp.php 1 week ago
mail-chimp-api.php
237 lines
1 <?php
2
3 namespace ElementsKit_Lite\Widgets\Mail_Chimp;
4
5 defined('ABSPATH') || exit;
6
7 use Elementor\ElementsKit_Widget_Mail_Chimp_Handler;
8 use ElementsKit_Lite\Core\Handler_Api;
9
10 class Mail_Chimp_Api extends Handler_Api {
11
12 const LISTS_CACHE_PREFIX = 'ekit_mailchimp_lists_';
13 const LISTS_CACHE_TTL = WEEK_IN_SECONDS;
14 const LISTS_REFRESH_LOCK = 30;
15 const LISTS_FAILURE_BACKOFF = 5 * MINUTE_IN_SECONDS;
16
17 public function config() {
18 $this->prefix = 'widget/mailchimp';
19 $this->param = '';
20 }
21
22 /**
23 * Get Mailchimp audiences, using the last known good response while it is stale.
24 *
25 * Remote requests are only made when explicitly allowed by the caller. This prevents
26 * Elementor frontend rendering from generating Mailchimp API traffic.
27 *
28 * @param string $token Mailchimp API key.
29 * @param bool $allow_refresh Whether a stale cache may be refreshed.
30 *
31 * @return array Audience IDs keyed to audience names.
32 */
33 public static function get_lists($token, $allow_refresh = false) {
34 $server_prefix = self::get_server_prefix($token);
35
36 if ('' === $server_prefix) {
37 return [];
38 }
39
40 $cache_key = self::get_lists_cache_key($token);
41 $cached = get_transient($cache_key);
42 $lists = (is_array($cached) && isset($cached['lists']) && is_array($cached['lists'])) ? $cached['lists'] : [];
43 $is_fresh = is_array($cached) && !empty($cached['fresh_until']) && $cached['fresh_until'] >= time();
44
45 if (!$is_fresh && $allow_refresh && false === get_transient($cache_key . '_backoff')) {
46 // Avoid duplicate Mailchimp requests when several editor requests arrive together.
47 set_transient($cache_key . '_backoff', 1, self::LISTS_REFRESH_LOCK);
48 $fetched = self::fetch_lists($token, $server_prefix, $cache_key);
49
50 if (is_array($fetched)) {
51 $lists = $fetched;
52 }
53 }
54
55 return $lists;
56 }
57
58 private static function get_server_prefix($token) {
59 $separator = strrpos($token, '-');
60
61 if (false === $separator) {
62 return '';
63 }
64
65 $server_prefix = substr($token, $separator + 1);
66
67 return preg_match('/^[a-z0-9]+$/i', $server_prefix) ? strtolower($server_prefix) : '';
68 }
69
70 private static function get_lists_cache_key($token) {
71 return self::LISTS_CACHE_PREFIX . md5($token);
72 }
73
74 /**
75 * Build the full Mailchimp API request (URL + headers) for a given path.
76 *
77 * @param string $server_prefix Mailchimp datacenter prefix (e.g. "us21").
78 * @param string $token Mailchimp API key.
79 * @param string $path API path relative to the /3.0/ base.
80 *
81 * @return array{url: string, headers: array}
82 */
83 private static function get_api_request($server_prefix, $token, $path) {
84 return [
85 'url' => 'https://' . $server_prefix . '.api.mailchimp.com/3.0/' . ltrim($path, '/'),
86 'headers' => [
87 'Authorization' => 'apikey ' . $token,
88 'Content-Type' => 'application/json; charset=utf-8',
89 ],
90 ];
91 }
92
93 private static function fetch_lists($token, $server_prefix, $cache_key) {
94 $request = self::get_api_request($server_prefix, $token, 'lists?count=1000&fields=lists.id,lists.name');
95
96 $response = wp_remote_get(
97 $request['url'],
98 [
99 'timeout' => 10,
100 'headers' => $request['headers'],
101 ]
102 );
103
104 if (is_wp_error($response) || 200 !== wp_remote_retrieve_response_code($response)) {
105 set_transient($cache_key . '_backoff', 1, self::LISTS_FAILURE_BACKOFF);
106 return null;
107 }
108
109 $body = json_decode(wp_remote_retrieve_body($response), true);
110 $lists = [];
111
112 if (!empty($body['lists']) && is_array($body['lists'])) {
113 foreach ($body['lists'] as $list) {
114 if (isset($list['id'], $list['name'])) {
115 $lists[$list['id']] = $list['name'];
116 }
117 }
118 }
119
120 $fresh_for = max(1, (int) apply_filters('ekit_mailchimp_lists_cache_expiration', 6 * HOUR_IN_SECONDS));
121
122 set_transient(
123 $cache_key,
124 [
125 'lists' => $lists,
126 'fresh_until' => time() + $fresh_for,
127 ],
128 max(self::LISTS_CACHE_TTL, $fresh_for)
129 );
130 delete_transient($cache_key . '_backoff');
131
132 return $lists;
133 }
134
135 /**
136 * Handle a Mailchimp subscribe request from the frontend form.
137 *
138 * Validates the request (nonce, API key, list, email) before ever building
139 * the payload or contacting Mailchimp, so invalid requests fail fast without
140 * doing unnecessary work.
141 *
142 * @return array{success: array, error: array|string}
143 */
144 public function get_sendmail(){
145 // Get only the GET parameters
146 $params = isset($this->request['GET']) ? $this->request['GET'] : $this->request->get_params();
147
148 $return = ['success' => [], 'error' => [] ];
149
150 $nonce = $this->request->get_header( 'X-WP-Nonce' );
151 if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
152 $return['error'] = esc_html__( 'Security check failed. Please refresh the page and try again.', 'elementskit-lite' );
153 return $return;
154 }
155
156 $dataApi = ElementsKit_Widget_Mail_Chimp_Handler::get_data();
157
158 $token = isset($dataApi['token']) ? $dataApi['token'] : '';
159 $listed = isset($params['listed']) ? sanitize_text_field($params['listed']) : '';
160 $email = isset($params['email']) ? sanitize_email($params['email']) : '';
161
162 $server_prefix = self::get_server_prefix($token);
163 if ('' === $server_prefix) {
164 $return['error'] = esc_html__( 'Please set API Key into Dashboard User Data. ', 'elementskit-lite' );
165 return $return;
166 }
167
168 if ('' === $listed) {
169 $return['error'] = esc_html__( 'Please select a MailChimp list.', 'elementskit-lite' );
170 return $return;
171 }
172
173 if ('' === $email || ! is_email($email)) {
174 $return['error'] = esc_html__( 'Please provide a valid email address.', 'elementskit-lite' );
175 return $return;
176 }
177
178 // Build merge fields dynamically from remaining parameters.
179 static $reserved_fields = ['listed', 'double_opt_in', 'email', 'action'];
180 $merge_fields = [];
181
182 foreach ( array_diff_key( $params, array_flip( $reserved_fields ) ) as $key => $value ) {
183 if ( $value !== '' ) {
184 // Convert field name to uppercase Mailchimp merge tag.
185 $merge_fields[ strtoupper( $key ) ] = sanitize_text_field( $value );
186 }
187 }
188
189 $data = [
190 'email_address' => $email,
191 ];
192
193 if ( ! empty( $merge_fields ) ) {
194 $data['merge_fields'] = $merge_fields;
195 }
196
197 $data['status'] = ( ! empty( $params['double_opt_in'] ) && 'yes' === $params['double_opt_in'] )
198 ? 'pending'
199 : 'subscribed';
200
201 $request = self::get_api_request($server_prefix, $token, 'lists/' . rawurlencode($listed) . '/members/');
202
203 $response = wp_remote_post( $request['url'], [
204 'method' => 'POST',
205 'data_format' => 'body',
206 'timeout' => 45,
207 'headers' => $request['headers'],
208 'body' => wp_json_encode($data)
209 ]
210 );
211
212 /* handle Mailchimp response */
213 if ( is_wp_error( $response ) ) {
214 $return['error'] = 'Something went wrong: ' . $response->get_error_message();
215 return $return;
216 }
217
218 $code = wp_remote_retrieve_response_code( $response );
219 $body = wp_remote_retrieve_body( $response );
220 $decoded = json_decode( $body, true );
221
222 if ( $code >= 400 ) {
223 if ( is_array( $decoded ) && ! empty( $decoded['title'] ) ) {
224 $return['error'] = $decoded['title'];
225 } else {
226 // Avoid echoing raw/unstructured response bodies back to the client.
227 $return['error'] = esc_html__( 'Mailchimp request failed. Please try again later.', 'elementskit-lite' );
228 }
229 } else {
230 // keep the original wp_remote_post response so JS can parse response.success.body
231 $return['success'] = $response;
232 }
233
234 return $return;
235 }
236 }
237