Newsletter - Version 7.5.5

Version Description

  • Fixed access control on form manager base class
Download this release

Release Info

Developer satollo
Plugin Icon 128x128 Newsletter
Version 7.5.5
Comparing to
See all releases

Code changes from version 7.5.4 to 7.5.5

Files changed (4) hide show
  1. includes/addon.php +396 -396
  2. plugin.php +2 -2
  3. profile/profile.php +494 -494
  4. readme.txt +5 -1
includes/addon.php CHANGED
@@ -1,396 +1,396 @@
1
- <?php
2
-
3
- /**
4
- * User by add-ons as base-class.
5
- */
6
- class NewsletterAddon {
7
-
8
- var $logger;
9
- var $admin_logger;
10
- var $name;
11
- var $options;
12
- var $version;
13
- var $labels;
14
-
15
- public function __construct($name, $version = '0.0.0') {
16
- $this->name = $name;
17
- $this->version = $version;
18
- if (is_admin()) {
19
- $old_version = get_option('newsletter_' . $name . '_version');
20
- if ($version !== $old_version) {
21
- $this->upgrade($old_version === false);
22
- update_option('newsletter_' . $name . '_version', $version, false);
23
- }
24
- }
25
- add_action('newsletter_init', array($this, 'init'));
26
- //Load translations from specific addon /languages/ directory
27
- load_plugin_textdomain('newsletter-' . $this->name, false, 'newsletter-' . $this->name . '/languages/');
28
- }
29
-
30
- /**
31
- * Method to be overridden and invoked on version change or on first install.
32
- *
33
- * @param bool $first_install
34
- */
35
- function upgrade($first_install = false) {
36
-
37
- }
38
-
39
- /**
40
- * Method to be overridden to initialize the add-on. It is invoked when Newsletter
41
- * fires the <code>newsletter_init</code> event.
42
- */
43
- function init() {
44
-
45
- }
46
-
47
- function get_current_language() {
48
- return Newsletter::instance()->get_current_language();
49
- }
50
-
51
- function is_all_languages() {
52
- return Newsletter::instance()->is_all_languages();
53
- }
54
-
55
- function is_allowed() {
56
- return Newsletter::instance()->is_allowed();
57
- }
58
-
59
- function get_languages() {
60
- return Newsletter::instance()->get_languages();
61
- }
62
-
63
- function is_multilanguage() {
64
- return Newsletter::instance()->is_multilanguage();
65
- }
66
-
67
- /**
68
- * General logger for this add-on.
69
- *
70
- * @return NewsletterLogger
71
- */
72
- function get_logger() {
73
- if (!$this->logger) {
74
- $this->logger = new NewsletterLogger($this->name);
75
- }
76
- return $this->logger;
77
- }
78
-
79
- /**
80
- * Specific logger for administrator actions.
81
- *
82
- * @return NewsletterLogger
83
- */
84
- function get_admin_logger() {
85
- if (!$this->admin_logger) {
86
- $this->admin_logger = new NewsletterLogger($this->name . '-admin');
87
- }
88
- return $this->admin_logger;
89
- }
90
-
91
- /**
92
- * Loads and prepares the options. It can be used to late initialize the options to save some resources on
93
- * add-ons which do not need to do something on each page load.
94
- */
95
- function setup_options() {
96
- if ($this->options) {
97
- return;
98
- }
99
- $this->options = get_option('newsletter_' . $this->name, []);
100
- }
101
-
102
- /**
103
- * Retrieve the stored options, merged with the specified language set.
104
- *
105
- * @param string $language
106
- * @return array
107
- */
108
- function get_options($language = '') {
109
- if ($language) {
110
- return array_merge(get_option('newsletter_' . $this->name, []), get_option('newsletter_' . $this->name . '_' . $language, []));
111
- } else {
112
- return get_option('newsletter_' . $this->name, []);
113
- }
114
- }
115
-
116
- /**
117
- * Saved the options under the correct keys and update the internal $options
118
- * property.
119
- * @param array $options
120
- */
121
- function save_options($options, $language = '') {
122
- if ($language) {
123
- update_option('newsletter_' . $this->name . '_' . $language, $options);
124
- } else {
125
- update_option('newsletter_' . $this->name, $options);
126
- $this->options = $options;
127
- }
128
- }
129
-
130
- function merge_defaults($defaults) {
131
- $options = get_option('newsletter_' . $this->name, []);
132
- $options = array_merge($defaults, $options);
133
- $this->save_options($options);
134
- }
135
-
136
- /**
137
- *
138
- */
139
- function setup_labels() {
140
- if (!$this->labels) {
141
- $labels = [];
142
- }
143
- }
144
-
145
- function get_label($key) {
146
- if (!$this->options)
147
- $this->setup_options();
148
-
149
- if (!empty($this->options[$key])) {
150
- return $this->options[$key];
151
- }
152
-
153
- if (!$this->labels)
154
- $this->setup_labels();
155
-
156
- // We assume the required key is defined. If not there is an error elsewhere.
157
- return $this->labels[$key];
158
- }
159
-
160
- /**
161
- * Equivalent to $wpdb->query() but logs the event in case of error.
162
- *
163
- * @global wpdb $wpdb
164
- * @param string $query
165
- */
166
- function query($query) {
167
- global $wpdb;
168
-
169
- $r = $wpdb->query($query);
170
- if ($r === false) {
171
- $logger = $this->get_logger();
172
- $logger->fatal($query);
173
- $logger->fatal($wpdb->last_error);
174
- }
175
- return $r;
176
- }
177
-
178
- }
179
-
180
- /**
181
- * Used by mailer add-ons as base-class. Some specific options collected by the mailer
182
- * are interpreted automatically.
183
- *
184
- * They are:
185
- *
186
- * `enabled` if not empty it means the mailer is active and should be registered
187
- *
188
- * The options are set up in the constructor, there is no need to setup them later.
189
- */
190
- class NewsletterMailerAddon extends NewsletterAddon {
191
-
192
- var $enabled = false;
193
- var $menu_title = null;
194
- var $menu_description = null;
195
- var $dir = '';
196
-
197
- function __construct($name, $version = '0.0.0', $dir = '') {
198
- parent::__construct($name, $version);
199
- $this->dir = $dir;
200
- $this->setup_options();
201
- $this->enabled = !empty($this->options['enabled']);
202
- }
203
-
204
- /**
205
- * This method must be called as `parent::init()` is overridden.
206
- */
207
- function init() {
208
- parent::init();
209
- add_action('newsletter_register_mailer', function () {
210
- if ($this->enabled) {
211
- Newsletter::instance()->register_mailer($this->get_mailer());
212
- }
213
- });
214
-
215
- if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && current_user_can('administrator')) {
216
- add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
217
- add_filter('newsletter_menu_settings', [$this, 'hook_newsletter_menu_settings']);
218
- }
219
- }
220
-
221
- function hook_newsletter_menu_settings($entries) {
222
- $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
223
- return $entries;
224
- }
225
-
226
- function hook_admin_menu() {
227
- add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'manage_options', 'newsletter_' . $this->name . '_index',
228
- function () {
229
- require $this->dir . '/index.php';
230
- }
231
- );
232
- }
233
-
234
- /**
235
- * Must return an implementation of NewsletterMailer.
236
- * @return NewsletterMailer
237
- */
238
- function get_mailer() {
239
- return null;
240
- }
241
-
242
- function get_last_run() {
243
- return get_option('newsletter_' . $this->name . '_last_run', 0);
244
- }
245
-
246
- function save_last_run($time) {
247
- update_option('newsletter_' . $this->name . '_last_run', $time);
248
- }
249
-
250
- function save_options($options, $language = '') {
251
- parent::save_options($options, $language);
252
- $this->enabled = !empty($options['enabled']);
253
- }
254
-
255
- /**
256
- * Returns a TNP_Mailer_Message built to send a test message to the <code>$to</code>
257
- * email address.
258
- *
259
- * @param string $to
260
- * @param string $subject
261
- * @return TNP_Mailer_Message
262
- */
263
- static function get_test_message($to, $subject = '', $type = '') {
264
- $message = new TNP_Mailer_Message();
265
- $message->to = $to;
266
- $message->to_name = '';
267
- if (empty($type) || $type == 'html') {
268
- $message->body = file_get_contents(NEWSLETTER_DIR . '/includes/test-message.html');
269
- $message->body = str_replace('{plugin_url}', NEWSLETTER_URL, $message->body);
270
- }
271
-
272
- if (empty($type) || $type == 'text') {
273
- $message->body_text = 'This is the TEXT version of a test message. You should see this message only if you email client does not support the rich text (HTML) version.';
274
- }
275
-
276
- $message->headers['X-Newsletter-Email-Id'] = '0';
277
-
278
- if (empty($subject)) {
279
- $message->subject = '[' . get_option('blogname') . '] Test message from Newsletter (' . date(DATE_ISO8601) . ')';
280
- } else {
281
- $message->subject = $subject;
282
- }
283
-
284
- if ($type) {
285
- $message->subject .= ' - ' . $type . ' only';
286
- }
287
-
288
- $message->from = Newsletter::instance()->options['sender_email'];
289
- $message->from_name = Newsletter::instance()->options['sender_name'];
290
- return $message;
291
- }
292
-
293
- /**
294
- * Returns a set of test messages to be sent to the specified email address. Used for
295
- * turbo mode tests. Each message has a different generated subject.
296
- *
297
- * @param string $to The destination mailbox
298
- * @param int $count Number of message objects to create
299
- * @return TNP_Mailer_Message[]
300
- */
301
- function get_test_messages($to, $count, $type = '') {
302
- $messages = array();
303
- for ($i = 0; $i < $count; $i++) {
304
- $messages[] = self::get_test_message($to, '[' . get_option('blogname') . '] Test message ' . ($i + 1) . ' from Newsletter (' . date(DATE_ISO8601) . ')', $type);
305
- }
306
- return $messages;
307
- }
308
-
309
- }
310
-
311
- class NewsletterFormManagerAddon extends NewsletterAddon {
312
-
313
- var $menu_title = null;
314
- var $menu_description = null;
315
- var $dir = '';
316
-
317
- function __construct($name, $version, $dir) {
318
- parent::__construct($name, $version);
319
- $this->dir = $dir;
320
- $this->setup_options();
321
- }
322
-
323
- function init() {
324
- parent::init();
325
-
326
- if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && Newsletter::instance()->is_allowed()) {
327
- add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
328
- add_filter('newsletter_menu_subscription', [$this, 'hook_newsletter_menu_subscription']);
329
- }
330
- }
331
-
332
- function hook_newsletter_menu_subscription($entries) {
333
- $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
334
- return $entries;
335
- }
336
-
337
- function hook_admin_menu() {
338
- add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'manage_options', 'newsletter_' . $this->name . '_index',
339
- function () {
340
- require $this->dir . '/admin/index.php';
341
- }
342
- );
343
- }
344
-
345
- /**
346
- * Returns a lists of representations of forms available in the plugin subject of integration.
347
- * Usually the $fields is not set up on returned objects.
348
- * Must be implemented.
349
- *
350
- * @return TNP_FormManager_Form[] List of forms by 3rd party plugin
351
- */
352
- function get_forms() {
353
- return [];
354
- }
355
-
356
- /**
357
- * Build a form general representation of a real form from a form manager plugin extracting
358
- * only the data required to integrate. The form id is domain of the form manager plugin, so it can be
359
- * anything.
360
- * Must be implemented.
361
- *
362
- * @param mixed $form_id
363
- * @return TNP_FormManager_Form
364
- */
365
- function get_form($form_id) {
366
- return null;
367
- }
368
-
369
- /**
370
- * Saves the form mapping and integration settings.
371
- * @param mixed $form_id
372
- * @param array $data
373
- */
374
- public function save_form_options($form_id, $data) {
375
- update_option('newsletter_' . $this->name . '_' . $form_id, $data, false);
376
- }
377
-
378
- /**
379
- * Gets the form mapping and integration settings. Returns an empty array if the dataset is missing.
380
- * @param mixed $form_id
381
- * @return array
382
- */
383
- public function get_form_options($form_id) {
384
- return get_option('newsletter_' . $this->name . '_' . $form_id, []);
385
- }
386
-
387
- }
388
-
389
- class TNP_FormManager_Form {
390
-
391
- var $id = null;
392
- var $title = '';
393
- var $fields = [];
394
- var $connected = false;
395
-
396
- }
1
+ <?php
2
+
3
+ /**
4
+ * User by add-ons as base-class.
5
+ */
6
+ class NewsletterAddon {
7
+
8
+ var $logger;
9
+ var $admin_logger;
10
+ var $name;
11
+ var $options;
12
+ var $version;
13
+ var $labels;
14
+
15
+ public function __construct($name, $version = '0.0.0') {
16
+ $this->name = $name;
17
+ $this->version = $version;
18
+ if (is_admin()) {
19
+ $old_version = get_option('newsletter_' . $name . '_version');
20
+ if ($version !== $old_version) {
21
+ $this->upgrade($old_version === false);
22
+ update_option('newsletter_' . $name . '_version', $version, false);
23
+ }
24
+ }
25
+ add_action('newsletter_init', array($this, 'init'));
26
+ //Load translations from specific addon /languages/ directory
27
+ load_plugin_textdomain('newsletter-' . $this->name, false, 'newsletter-' . $this->name . '/languages/');
28
+ }
29
+
30
+ /**
31
+ * Method to be overridden and invoked on version change or on first install.
32
+ *
33
+ * @param bool $first_install
34
+ */
35
+ function upgrade($first_install = false) {
36
+
37
+ }
38
+
39
+ /**
40
+ * Method to be overridden to initialize the add-on. It is invoked when Newsletter
41
+ * fires the <code>newsletter_init</code> event.
42
+ */
43
+ function init() {
44
+
45
+ }
46
+
47
+ function get_current_language() {
48
+ return Newsletter::instance()->get_current_language();
49
+ }
50
+
51
+ function is_all_languages() {
52
+ return Newsletter::instance()->is_all_languages();
53
+ }
54
+
55
+ function is_allowed() {
56
+ return Newsletter::instance()->is_allowed();
57
+ }
58
+
59
+ function get_languages() {
60
+ return Newsletter::instance()->get_languages();
61
+ }
62
+
63
+ function is_multilanguage() {
64
+ return Newsletter::instance()->is_multilanguage();
65
+ }
66
+
67
+ /**
68
+ * General logger for this add-on.
69
+ *
70
+ * @return NewsletterLogger
71
+ */
72
+ function get_logger() {
73
+ if (!$this->logger) {
74
+ $this->logger = new NewsletterLogger($this->name);
75
+ }
76
+ return $this->logger;
77
+ }
78
+
79
+ /**
80
+ * Specific logger for administrator actions.
81
+ *
82
+ * @return NewsletterLogger
83
+ */
84
+ function get_admin_logger() {
85
+ if (!$this->admin_logger) {
86
+ $this->admin_logger = new NewsletterLogger($this->name . '-admin');
87
+ }
88
+ return $this->admin_logger;
89
+ }
90
+
91
+ /**
92
+ * Loads and prepares the options. It can be used to late initialize the options to save some resources on
93
+ * add-ons which do not need to do something on each page load.
94
+ */
95
+ function setup_options() {
96
+ if ($this->options) {
97
+ return;
98
+ }
99
+ $this->options = get_option('newsletter_' . $this->name, []);
100
+ }
101
+
102
+ /**
103
+ * Retrieve the stored options, merged with the specified language set.
104
+ *
105
+ * @param string $language
106
+ * @return array
107
+ */
108
+ function get_options($language = '') {
109
+ if ($language) {
110
+ return array_merge(get_option('newsletter_' . $this->name, []), get_option('newsletter_' . $this->name . '_' . $language, []));
111
+ } else {
112
+ return get_option('newsletter_' . $this->name, []);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Saved the options under the correct keys and update the internal $options
118
+ * property.
119
+ * @param array $options
120
+ */
121
+ function save_options($options, $language = '') {
122
+ if ($language) {
123
+ update_option('newsletter_' . $this->name . '_' . $language, $options);
124
+ } else {
125
+ update_option('newsletter_' . $this->name, $options);
126
+ $this->options = $options;
127
+ }
128
+ }
129
+
130
+ function merge_defaults($defaults) {
131
+ $options = get_option('newsletter_' . $this->name, []);
132
+ $options = array_merge($defaults, $options);
133
+ $this->save_options($options);
134
+ }
135
+
136
+ /**
137
+ *
138
+ */
139
+ function setup_labels() {
140
+ if (!$this->labels) {
141
+ $labels = [];
142
+ }
143
+ }
144
+
145
+ function get_label($key) {
146
+ if (!$this->options)
147
+ $this->setup_options();
148
+
149
+ if (!empty($this->options[$key])) {
150
+ return $this->options[$key];
151
+ }
152
+
153
+ if (!$this->labels)
154
+ $this->setup_labels();
155
+
156
+ // We assume the required key is defined. If not there is an error elsewhere.
157
+ return $this->labels[$key];
158
+ }
159
+
160
+ /**
161
+ * Equivalent to $wpdb->query() but logs the event in case of error.
162
+ *
163
+ * @global wpdb $wpdb
164
+ * @param string $query
165
+ */
166
+ function query($query) {
167
+ global $wpdb;
168
+
169
+ $r = $wpdb->query($query);
170
+ if ($r === false) {
171
+ $logger = $this->get_logger();
172
+ $logger->fatal($query);
173
+ $logger->fatal($wpdb->last_error);
174
+ }
175
+ return $r;
176
+ }
177
+
178
+ }
179
+
180
+ /**
181
+ * Used by mailer add-ons as base-class. Some specific options collected by the mailer
182
+ * are interpreted automatically.
183
+ *
184
+ * They are:
185
+ *
186
+ * `enabled` if not empty it means the mailer is active and should be registered
187
+ *
188
+ * The options are set up in the constructor, there is no need to setup them later.
189
+ */
190
+ class NewsletterMailerAddon extends NewsletterAddon {
191
+
192
+ var $enabled = false;
193
+ var $menu_title = null;
194
+ var $menu_description = null;
195
+ var $dir = '';
196
+
197
+ function __construct($name, $version = '0.0.0', $dir = '') {
198
+ parent::__construct($name, $version);
199
+ $this->dir = $dir;
200
+ $this->setup_options();
201
+ $this->enabled = !empty($this->options['enabled']);
202
+ }
203
+
204
+ /**
205
+ * This method must be called as `parent::init()` is overridden.
206
+ */
207
+ function init() {
208
+ parent::init();
209
+ add_action('newsletter_register_mailer', function () {
210
+ if ($this->enabled) {
211
+ Newsletter::instance()->register_mailer($this->get_mailer());
212
+ }
213
+ });
214
+
215
+ if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && current_user_can('administrator')) {
216
+ add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
217
+ add_filter('newsletter_menu_settings', [$this, 'hook_newsletter_menu_settings']);
218
+ }
219
+ }
220
+
221
+ function hook_newsletter_menu_settings($entries) {
222
+ $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
223
+ return $entries;
224
+ }
225
+
226
+ function hook_admin_menu() {
227
+ add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'manage_options', 'newsletter_' . $this->name . '_index',
228
+ function () {
229
+ require $this->dir . '/index.php';
230
+ }
231
+ );
232
+ }
233
+
234
+ /**
235
+ * Must return an implementation of NewsletterMailer.
236
+ * @return NewsletterMailer
237
+ */
238
+ function get_mailer() {
239
+ return null;
240
+ }
241
+
242
+ function get_last_run() {
243
+ return get_option('newsletter_' . $this->name . '_last_run', 0);
244
+ }
245
+
246
+ function save_last_run($time) {
247
+ update_option('newsletter_' . $this->name . '_last_run', $time);
248
+ }
249
+
250
+ function save_options($options, $language = '') {
251
+ parent::save_options($options, $language);
252
+ $this->enabled = !empty($options['enabled']);
253
+ }
254
+
255
+ /**
256
+ * Returns a TNP_Mailer_Message built to send a test message to the <code>$to</code>
257
+ * email address.
258
+ *
259
+ * @param string $to
260
+ * @param string $subject
261
+ * @return TNP_Mailer_Message
262
+ */
263
+ static function get_test_message($to, $subject = '', $type = '') {
264
+ $message = new TNP_Mailer_Message();
265
+ $message->to = $to;
266
+ $message->to_name = '';
267
+ if (empty($type) || $type == 'html') {
268
+ $message->body = file_get_contents(NEWSLETTER_DIR . '/includes/test-message.html');
269
+ $message->body = str_replace('{plugin_url}', NEWSLETTER_URL, $message->body);
270
+ }
271
+
272
+ if (empty($type) || $type == 'text') {
273
+ $message->body_text = 'This is the TEXT version of a test message. You should see this message only if you email client does not support the rich text (HTML) version.';
274
+ }
275
+
276
+ $message->headers['X-Newsletter-Email-Id'] = '0';
277
+
278
+ if (empty($subject)) {
279
+ $message->subject = '[' . get_option('blogname') . '] Test message from Newsletter (' . date(DATE_ISO8601) . ')';
280
+ } else {
281
+ $message->subject = $subject;
282
+ }
283
+
284
+ if ($type) {
285
+ $message->subject .= ' - ' . $type . ' only';
286
+ }
287
+
288
+ $message->from = Newsletter::instance()->options['sender_email'];
289
+ $message->from_name = Newsletter::instance()->options['sender_name'];
290
+ return $message;
291
+ }
292
+
293
+ /**
294
+ * Returns a set of test messages to be sent to the specified email address. Used for
295
+ * turbo mode tests. Each message has a different generated subject.
296
+ *
297
+ * @param string $to The destination mailbox
298
+ * @param int $count Number of message objects to create
299
+ * @return TNP_Mailer_Message[]
300
+ */
301
+ function get_test_messages($to, $count, $type = '') {
302
+ $messages = array();
303
+ for ($i = 0; $i < $count; $i++) {
304
+ $messages[] = self::get_test_message($to, '[' . get_option('blogname') . '] Test message ' . ($i + 1) . ' from Newsletter (' . date(DATE_ISO8601) . ')', $type);
305
+ }
306
+ return $messages;
307
+ }
308
+
309
+ }
310
+
311
+ class NewsletterFormManagerAddon extends NewsletterAddon {
312
+
313
+ var $menu_title = null;
314
+ var $menu_description = null;
315
+ var $dir = '';
316
+
317
+ function __construct($name, $version, $dir) {
318
+ parent::__construct($name, $version);
319
+ $this->dir = $dir;
320
+ $this->setup_options();
321
+ }
322
+
323
+ function init() {
324
+ parent::init();
325
+
326
+ if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && Newsletter::instance()->is_allowed()) {
327
+ add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
328
+ add_filter('newsletter_menu_subscription', [$this, 'hook_newsletter_menu_subscription']);
329
+ }
330
+ }
331
+
332
+ function hook_newsletter_menu_subscription($entries) {
333
+ $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
334
+ return $entries;
335
+ }
336
+
337
+ function hook_admin_menu() {
338
+ add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'exist', 'newsletter_' . $this->name . '_index',
339
+ function () {
340
+ require $this->dir . '/admin/index.php';
341
+ }
342
+ );
343
+ }
344
+
345
+ /**
346
+ * Returns a lists of representations of forms available in the plugin subject of integration.
347
+ * Usually the $fields is not set up on returned objects.
348
+ * Must be implemented.
349
+ *
350
+ * @return TNP_FormManager_Form[] List of forms by 3rd party plugin
351
+ */
352
+ function get_forms() {
353
+ return [];
354
+ }
355
+
356
+ /**
357
+ * Build a form general representation of a real form from a form manager plugin extracting
358
+ * only the data required to integrate. The form id is domain of the form manager plugin, so it can be
359
+ * anything.
360
+ * Must be implemented.
361
+ *
362
+ * @param mixed $form_id
363
+ * @return TNP_FormManager_Form
364
+ */
365
+ function get_form($form_id) {
366
+ return null;
367
+ }
368
+
369
+ /**
370
+ * Saves the form mapping and integration settings.
371
+ * @param mixed $form_id
372
+ * @param array $data
373
+ */
374
+ public function save_form_options($form_id, $data) {
375
+ update_option('newsletter_' . $this->name . '_' . $form_id, $data, false);
376
+ }
377
+
378
+ /**
379
+ * Gets the form mapping and integration settings. Returns an empty array if the dataset is missing.
380
+ * @param mixed $form_id
381
+ * @return array
382
+ */
383
+ public function get_form_options($form_id) {
384
+ return get_option('newsletter_' . $this->name . '_' . $form_id, []);
385
+ }
386
+
387
+ }
388
+
389
+ class TNP_FormManager_Form {
390
+
391
+ var $id = null;
392
+ var $title = '';
393
+ var $fields = [];
394
+ var $connected = false;
395
+
396
+ }
plugin.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
- Version: 7.5.4
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
@@ -37,7 +37,7 @@ if (version_compare(phpversion(), '5.6', '<')) {
37
  return;
38
  }
39
 
40
- define('NEWSLETTER_VERSION', '7.5.4');
41
 
42
  global $newsletter, $wpdb;
43
 
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 7.5.5
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
37
  return;
38
  }
39
 
40
+ define('NEWSLETTER_VERSION', '7.5.5');
41
 
42
  global $newsletter, $wpdb;
43
 
profile/profile.php CHANGED
@@ -1,494 +1,494 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- class NewsletterProfile extends NewsletterModule {
6
-
7
- static $instance;
8
-
9
- /**
10
- * @return NewsletterProfile
11
- */
12
- static function instance() {
13
- if (self::$instance == null) {
14
- self::$instance = new NewsletterProfile();
15
- }
16
- return self::$instance;
17
- }
18
-
19
- function __construct() {
20
- parent::__construct('profile', '1.1.0');
21
- add_shortcode('newsletter_profile', array($this, 'shortcode_newsletter_profile'));
22
- add_filter('newsletter_replace', array($this, 'hook_newsletter_replace'), 10, 4);
23
- add_filter('newsletter_page_text', array($this, 'hook_newsletter_page_text'), 10, 3);
24
- add_action('newsletter_action', array($this, 'hook_newsletter_action'), 12, 3);
25
- }
26
-
27
- function hook_newsletter_action($action, $user, $email) {
28
-
29
- if (in_array($action, ['p', 'profile', 'pe', 'profile-save', 'profile_export', 'ps'])) {
30
- if (!$user || $user->status != TNP_User::STATUS_CONFIRMED) {
31
-
32
- $this->dienow(__('The subscriber was not found or is not confirmed.', 'newsletter'), '', 404);
33
- }
34
- }
35
-
36
- switch ($action) {
37
- case 'profile':
38
- case 'p':
39
- case 'pe':
40
-
41
- $profile_url = $this->build_message_url($this->options['url'], 'profile', $user, $email);
42
- $profile_url = apply_filters('newsletter_profile_url', $profile_url, $user);
43
-
44
- wp_redirect($profile_url);
45
- die();
46
-
47
- break;
48
-
49
- case 'profile-save':
50
- case 'ps':
51
- $res = $this->save_profile($user);
52
- if (is_wp_error($res)) {
53
- wp_redirect($this->build_message_url($this->options['url'], 'profile', $user, $email, $res->get_error_message()));
54
- die();
55
- }
56
-
57
- wp_redirect($this->build_message_url($this->options['url'], 'profile', $user, $email, $res));
58
- die();
59
- break;
60
-
61
- case 'profile_export':
62
- header('Content-Type: application/json;charset=UTF-8');
63
- echo $this->to_json($user);
64
- die();
65
- }
66
- }
67
-
68
- /**
69
- *
70
- * @param stdClass $user
71
- */
72
- function get_profile_export_url($user) {
73
- return $this->build_action_url('profile_export', $user);
74
- }
75
-
76
- /**
77
- * URL to the subscriber profile edit action. This URL MUST NEVER be changed by
78
- * 3rd party plugins. Plugins can change the final URL after the action has been executed using the
79
- * <code>newsletter_profile_url</code> filter.
80
- *
81
- * @param stdClass $user
82
- */
83
- function get_profile_url($user, $email = null) {
84
- return $this->build_action_url('profile', $user, $email);
85
- }
86
-
87
- function hook_newsletter_replace($text, $user, $email, $html = true) {
88
- if (!$user) {
89
- $text = $this->replace_url($text, 'PROFILE_URL', $this->build_action_url('nul'));
90
- return $text;
91
- }
92
-
93
- // Profile edit page URL and link
94
- $url = $this->get_profile_url($user, $email);
95
- $text = $this->replace_url($text, 'profile_url', $url);
96
- // Profile export URL and link
97
- $url = $this->get_profile_export_url($user);
98
- $text = $this->replace_url($text, 'profile_export_url', $url);
99
-
100
- if (strpos($text, '{profile_form}') !== false) {
101
- $text = str_replace('{profile_form}', $this->get_profile_form($user), $text);
102
- }
103
- return $text;
104
- }
105
-
106
- /**
107
- *
108
- * @param type $text
109
- * @param type $key
110
- * @param TNP_User $user
111
- * @return string
112
- */
113
- function hook_newsletter_page_text($text, $key, $user) {
114
- if ($key == 'profile') {
115
- if (!$user || $user->status == TNP_User::STATUS_UNSUBSCRIBED) {
116
- return 'Subscriber not found.';
117
- }
118
- $options = $this->get_options('main', $this->get_current_language($user));
119
- return $options['text'];
120
- }
121
- return $text;
122
- }
123
-
124
- function shortcode_newsletter_profile($attrs, $content) {
125
- $user = $this->check_user();
126
-
127
- if (empty($user)) {
128
- if (empty($content)) {
129
- return __('Subscriber not found.', 'newsletter');
130
- } else {
131
- return $content;
132
- }
133
- }
134
-
135
- return $this->get_profile_form($user);
136
- }
137
-
138
- function to_json($user) {
139
- global $wpdb;
140
-
141
-
142
- $fields = array('name', 'surname', 'sex', 'created', 'ip', 'email');
143
- $data = array(
144
- 'email' => $user->email,
145
- 'name' => $user->name,
146
- 'last_name' => $user->surname,
147
- 'gender' => $user->sex,
148
- 'created' => $user->created,
149
- 'ip' => $user->ip,
150
- );
151
-
152
- // Lists
153
- $data['lists'] = array();
154
-
155
- $lists = $this->get_lists_public();
156
- foreach ($lists as $list) {
157
- $field = 'list_' . $list->id;
158
- if ($user->$field == 1) {
159
- $data['lists'][] = $list->name;
160
- }
161
- }
162
-
163
- // Profile
164
- $options_profile = get_option('newsletter_profile', array());
165
- $data['profiles'] = array();
166
- for ($i = 1; $i < NEWSLETTER_PROFILE_MAX; $i++) {
167
- $field = 'profile_' . $i;
168
- if ($options_profile[$field . '_status'] != 1 && $options_profile[$field . '_status'] != 2) {
169
- continue;
170
- }
171
- $data['profiles'][] = array('name' => $options_profile[$field], 'value' => $user->$field);
172
- }
173
-
174
- // Newsletters
175
- if ($this->options['export_newsletters']) {
176
- $sent = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_sent where user_id=%d order by email_id asc", $user->id));
177
- $newsletters = array();
178
- foreach ($sent as $item) {
179
- $action = 'none';
180
- if ($item->open == 1) {
181
- $action = 'read';
182
- } else if ($item->open == 2) {
183
- $action = 'click';
184
- }
185
-
186
- $email = $this->get_email($item->email_id);
187
- if (!$email) {
188
- continue;
189
- }
190
- // 'id'=>$item->email_id,
191
- $newsletters[] = array('subject' => $email->subject, 'action' => $action, 'sent' => date('Y-m-d h:i:s', $email->send_on));
192
- }
193
-
194
- $data['newsletters'] = $newsletters;
195
- }
196
-
197
- $extra = apply_filters('newsletter_profile_export_extra', array());
198
-
199
- $data = array_merge($extra, $data);
200
-
201
- return json_encode($data, JSON_PRETTY_PRINT);
202
- }
203
-
204
- /**
205
- * Build the profile editing form for the specified subscriber.
206
- *
207
- * @param TNP_User $user
208
- * @return string
209
- */
210
- function get_profile_form($user) {
211
- // Do not pay attention to option name here, it's a compatibility problem
212
-
213
- $language = $this->get_user_language($user);
214
- $options = NewsletterSubscription::instance()->get_options('profile', $language);
215
-
216
- $buffer = '';
217
-
218
- $buffer .= '<div class="tnp tnp-profile">';
219
- $buffer .= '<form action="' . $this->build_action_url('ps') . '" method="post">';
220
- $buffer .= '<input type="hidden" name="nk" value="' . esc_attr($user->id . '-' . $user->token) . '">';
221
-
222
- $buffer .= '<div class="tnp-field tnp-field-email">';
223
- $buffer .= '<label>' . esc_html($options['email']) . '</label>';
224
- $buffer .= '<input class="tnp-email" type="text" name="ne" required value="' . esc_attr($user->email) . '">';
225
- $buffer .= "</div>\n";
226
-
227
-
228
- if ($options['name_status'] >= 1) {
229
- $buffer .= '<div class="tnp-field tnp-field-firstname">';
230
- $buffer .= '<label>' . esc_html($options['name']) . '</label>';
231
- $buffer .= '<input class="tnp-firstname" type="text" name="nn" value="' . esc_attr($user->name) . '"' . ($options['name_rules'] == 1 ? ' required' : '') . '>';
232
- $buffer .= "</div>\n";
233
- }
234
-
235
- if ($options['surname_status'] >= 1) {
236
- $buffer .= '<div class="tnp-field tnp-field-lastname">';
237
- $buffer .= '<label>' . esc_html($options['surname']) . '</label>';
238
- $buffer .= '<input class="tnp-lastname" type="text" name="ns" value="' . esc_attr($user->surname) . '"' . ($options['surname_rules'] == 1 ? ' required' : '') . '>';
239
- $buffer .= "</div>\n";
240
- }
241
-
242
- if ($options['sex_status'] >= 1) {
243
- $buffer .= '<div class="tnp-field tnp-field-gender">';
244
- $buffer .= '<label>' . esc_html($options['sex']) . '</label>';
245
- $buffer .= '<select name="nx" class="tnp-gender"';
246
- if ($options['sex_rules']) {
247
- $buffer .= ' required ';
248
- }
249
- $buffer .= '>';
250
- if ($options['sex_rules']) {
251
- $buffer .= '<option value=""></option>';
252
- }
253
- $buffer .= '<option value="n"' . ($user->sex == 'n' ? ' selected' : '') . '>' . esc_html($options['sex_none']) . '</option>';
254
- $buffer .= '<option value="f"' . ($user->sex == 'f' ? ' selected' : '') . '>' . esc_html($options['sex_female']) . '</option>';
255
- $buffer .= '<option value="m"' . ($user->sex == 'm' ? ' selected' : '') . '>' . esc_html($options['sex_male']) . '</option>';
256
- $buffer .= '</select>';
257
- $buffer .= "</div>\n";
258
- }
259
-
260
- if ($this->is_multilanguage()) {
261
-
262
- $languages = $this->get_languages();
263
-
264
- $buffer .= '<div class="tnp-field tnp-field-language">';
265
- $buffer .= '<label>' . __('Language', 'newsletter') . '</label>';
266
- $buffer .= '<select name="nlng" class="tnp-language">';
267
-
268
- $buffer .= '<option value="" disabled ' . ( empty($user->language) ? ' selected' : '' ) . '>' . __('Select language', 'newsletter') . '</option>';
269
- foreach ($languages as $key => $l) {
270
- $buffer .= '<option value="' . $key . '"' . ( $user->language == $key ? ' selected' : '' ) . '>' . esc_html($l) . '</option>';
271
- }
272
-
273
- $buffer .= '</select>';
274
- $buffer .= "</div>\n";
275
- }
276
-
277
- // Profile
278
- $profiles = NewsletterSubscription::instance()->get_profiles_for_profile($user->language);
279
- foreach ($profiles as $profile) {
280
- $i = $profile->id; // I'm lazy
281
-
282
- $buffer .= '<div class="tnp-field tnp-field-profile">';
283
- $buffer .= '<label>' . esc_html($profile->name) . '</label>';
284
-
285
- $field = 'profile_' . $i;
286
-
287
- if ($profile->is_text()) {
288
- $buffer .= '<input class="tnp-profile tnp-profile-' . $i . '" type="text" name="np' . $i . '" value="' . esc_attr($user->$field) . '"' .
289
- ($profile->is_required() ? ' required' : '') . '>';
290
- }
291
-
292
- if ($profile->is_select()) {
293
- $buffer .= '<select class="tnp-profile tnp-profile-' . $i . '" name="np' . $i . '"' . ($profile->is_required() ? ' required' : '') . '>';
294
- foreach ($profile->options as $option) {
295
- $buffer .= '<option';
296
- if ($option == $user->$field) {
297
- $buffer .= ' selected';
298
- }
299
- $buffer .= '>' . esc_html($option) . '</option>';
300
- }
301
- $buffer .= '</select>';
302
- }
303
-
304
- $buffer .= "</div>\n";
305
- }
306
-
307
- // Lists
308
- $lists = $this->get_lists_for_profile($language);
309
- $tmp = '';
310
- foreach ($lists as $list) {
311
-
312
- $tmp .= '<div class="tnp-field tnp-field-list">';
313
- $tmp .= '<label><input class="tnp-list tnp-list-' . $list->id . '" type="checkbox" name="nl[]" value="' . $list->id . '"';
314
- $field = 'list_' . $list->id;
315
- if ($user->$field == 1) {
316
- $tmp .= ' checked';
317
- }
318
- $tmp .= '><span class="tnp-list-label">' . esc_html($list->name) . '</span></label>';
319
- $tmp .= "</div>\n";
320
- }
321
-
322
- if (!empty($tmp)) {
323
- $buffer .= '<div class="tnp-lists">' . "\n" . $tmp . "\n" . '</div>';
324
- }
325
-
326
- // Obsolete
327
- $extra = apply_filters('newsletter_profile_extra', array(), $user);
328
- foreach ($extra as $x) {
329
- $buffer .= '<div class="tnp-field">';
330
- $buffer .= '<label>' . $x['label'] . "</label>";
331
- $buffer .= $x['field'];
332
- $buffer .= "</div>\n";
333
- }
334
-
335
- $local_options = $this->get_options('', $language);
336
-
337
- // Privacy
338
- $privacy_url = NewsletterSubscription::instance()->get_privacy_url();
339
- if (!empty($local_options['privacy_label']) && !empty($privacy_url)) {
340
- $buffer .= '<div class="tnp-field tnp-field-privacy">';
341
- if ($privacy_url) {
342
- $buffer .= '<a href="' . $privacy_url . '" target="_blank">';
343
- }
344
-
345
- $buffer .= $local_options['privacy_label'];
346
-
347
- if ($privacy_url) {
348
- $buffer .= '</a>';
349
- }
350
- $buffer .= "</div>\n";
351
- }
352
-
353
- $buffer .= '<div class="tnp-field tnp-field-button">';
354
- $buffer .= '<input class="tnp-submit" type="submit" value="' . esc_attr($local_options['save_label']) . '">';
355
- $buffer .= "</div>\n";
356
-
357
- $buffer .= "</form>\n</div>\n";
358
-
359
- return $buffer;
360
- }
361
-
362
- /**
363
- * Saves the subscriber data extracting them from the $_REQUEST and for the
364
- * subscriber identified by the <code>$user</code> object.
365
- *
366
- * @return string|WP_Error If not an error the string represent the message to show
367
- */
368
- function save_profile($user) {
369
- global $wpdb;
370
-
371
- // Conatains the cleaned up user data to be saved
372
- $data = array();
373
- $data['id'] = $user->id;
374
-
375
- $options = $this->get_options('', $this->get_current_language($user));
376
- $options_profile = get_option('newsletter_profile', array());
377
- $options_main = get_option('newsletter_main', array());
378
-
379
- // Not an elegant interaction between modules but...
380
- $subscription_module = NewsletterSubscription::instance();
381
-
382
- require_once NEWSLETTER_INCLUDES_DIR . '/antispam.php';
383
-
384
- $antispam = NewsletterAntispam::instance();
385
-
386
- $email = $this->normalize_email(stripslashes($_REQUEST['ne']));
387
-
388
- if ($antispam->is_address_blacklisted($email)) {
389
- return new WP_Error('spam', 'That email address is not accepted');
390
- }
391
-
392
- if (!$email) {
393
- return new WP_Error('email', $options['error']);
394
- }
395
-
396
- $email_changed = ($email != $user->email);
397
-
398
- // If the email has been changed, check if it is available
399
- if ($email_changed) {
400
- $tmp = $this->get_user($email);
401
- if ($tmp != null && $tmp->id != $user->id) {
402
- return new WP_Error('inuse', $options['error']);
403
- }
404
- }
405
-
406
- if ($email_changed && $subscription_module->is_double_optin()) {
407
- set_transient('newsletter_user_' . $user->id . '_email', $email, DAY_IN_SECONDS);
408
- } else {
409
- $data['email'] = $email;
410
- }
411
-
412
- if (isset($_REQUEST['nn'])) {
413
- $data['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
414
- if ($antispam->is_spam_text($data['name'])) {
415
- return new WP_Error('spam', 'That name/surname');
416
- }
417
- }
418
- if (isset($_REQUEST['ns'])) {
419
- $data['surname'] = $this->normalize_name(stripslashes($_REQUEST['ns']));
420
- if ($antispam->is_spam_text($data['surname'])) {
421
- return new WP_Error('spam', 'That name/surname');
422
- }
423
- }
424
- if ($options_profile['sex_status'] >= 1) {
425
- $data['sex'] = $_REQUEST['nx'][0];
426
- // Wrong data injection check
427
- if ($data['sex'] != 'm' && $data['sex'] != 'f' && $data['sex'] != 'n') {
428
- die('Wrong sex field');
429
- }
430
- }
431
- if (isset($_REQUEST['nlng'])) {
432
- $languages = $this->get_languages();
433
- if (isset($languages[$_REQUEST['nlng']])) {
434
- $data['language'] = $_REQUEST['nlng'];
435
- }
436
- }
437
-
438
- // Lists. If not list is present or there is no list to choose or all are unchecked.
439
- $nl = array();
440
- if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
441
- $nl = $_REQUEST['nl'];
442
- }
443
-
444
- // Every possible list shown in the profile must be processed
445
- $lists = $this->get_lists_for_profile();
446
- foreach ($lists as $list) {
447
- $field_name = 'list_' . $list->id;
448
- $data[$field_name] = in_array($list->id, $nl) ? 1 : 0;
449
- }
450
-
451
- // Profile
452
- $profiles = $this->get_profiles_public();
453
- foreach ($profiles as $profile) {
454
- if (isset($_REQUEST['np' . $profile->id])) {
455
- $data['profile_' . $profile->id] = stripslashes($_REQUEST['np' . $profile->id]);
456
- }
457
- }
458
-
459
- // Feed by Mail service is saved here
460
- $data = apply_filters('newsletter_profile_save', $data);
461
-
462
- if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
463
- $data['status'] = TNP_User::STATUS_CONFIRMED;
464
- }
465
-
466
- $user = $this->save_user($data);
467
- $this->add_user_log($user, 'profile');
468
-
469
- // Send the activation again only if we use double opt-in, otherwise it has no meaning
470
- if ($email_changed && $subscription_module->is_double_optin()) {
471
- $user->email = $email;
472
- $subscription_module->send_activation_email($user);
473
- return $options['email_changed'];
474
- }
475
-
476
- return $options['saved'];
477
- }
478
-
479
- function admin_menu() {
480
- $this->add_admin_page('index', 'Profile');
481
- }
482
-
483
- // Patch to avoid conflicts with the "newsletter_profile" option of the subscription module
484
- // TODO: Fix it
485
- public function get_prefix($sub = '', $language = '') {
486
- if (empty($sub)) {
487
- $sub = 'main';
488
- }
489
- return parent::get_prefix($sub, $language);
490
- }
491
-
492
- }
493
-
494
- NewsletterProfile::instance();
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterProfile extends NewsletterModule {
6
+
7
+ static $instance;
8
+
9
+ /**
10
+ * @return NewsletterProfile
11
+ */
12
+ static function instance() {
13
+ if (self::$instance == null) {
14
+ self::$instance = new NewsletterProfile();
15
+ }
16
+ return self::$instance;
17
+ }
18
+
19
+ function __construct() {
20
+ parent::__construct('profile', '1.1.0');
21
+ add_shortcode('newsletter_profile', array($this, 'shortcode_newsletter_profile'));
22
+ add_filter('newsletter_replace', array($this, 'hook_newsletter_replace'), 10, 4);
23
+ add_filter('newsletter_page_text', array($this, 'hook_newsletter_page_text'), 10, 3);
24
+ add_action('newsletter_action', array($this, 'hook_newsletter_action'), 12, 3);
25
+ }
26
+
27
+ function hook_newsletter_action($action, $user, $email) {
28
+
29
+ if (in_array($action, ['p', 'profile', 'pe', 'profile-save', 'profile_export', 'ps'])) {
30
+ if (!$user || $user->status != TNP_User::STATUS_CONFIRMED) {
31
+
32
+ $this->dienow(__('The subscriber was not found or is not confirmed.', 'newsletter'), '', 404);
33
+ }
34
+ }
35
+
36
+ switch ($action) {
37
+ case 'profile':
38
+ case 'p':
39
+ case 'pe':
40
+
41
+ $profile_url = $this->build_message_url($this->options['url'], 'profile', $user, $email);
42
+ $profile_url = apply_filters('newsletter_profile_url', $profile_url, $user);
43
+
44
+ wp_redirect($profile_url);
45
+ die();
46
+
47
+ break;
48
+
49
+ case 'profile-save':
50
+ case 'ps':
51
+ $res = $this->save_profile($user);
52
+ if (is_wp_error($res)) {
53
+ wp_redirect($this->build_message_url($this->options['url'], 'profile', $user, $email, $res->get_error_message()));
54
+ die();
55
+ }
56
+
57
+ wp_redirect($this->build_message_url($this->options['url'], 'profile', $user, $email, $res));
58
+ die();
59
+ break;
60
+
61
+ case 'profile_export':
62
+ header('Content-Type: application/json;charset=UTF-8');
63
+ echo $this->to_json($user);
64
+ die();
65
+ }
66
+ }
67
+
68
+ /**
69
+ *
70
+ * @param stdClass $user
71
+ */
72
+ function get_profile_export_url($user) {
73
+ return $this->build_action_url('profile_export', $user);
74
+ }
75
+
76
+ /**
77
+ * URL to the subscriber profile edit action. This URL MUST NEVER be changed by
78
+ * 3rd party plugins. Plugins can change the final URL after the action has been executed using the
79
+ * <code>newsletter_profile_url</code> filter.
80
+ *
81
+ * @param stdClass $user
82
+ */
83
+ function get_profile_url($user, $email = null) {
84
+ return $this->build_action_url('profile', $user, $email);
85
+ }
86
+
87
+ function hook_newsletter_replace($text, $user, $email, $html = true) {
88
+ if (!$user) {
89
+ $text = $this->replace_url($text, 'PROFILE_URL', $this->build_action_url('nul'));
90
+ return $text;
91
+ }
92
+
93
+ // Profile edit page URL and link
94
+ $url = $this->get_profile_url($user, $email);
95
+ $text = $this->replace_url($text, 'profile_url', $url);
96
+ // Profile export URL and link
97
+ $url = $this->get_profile_export_url($user);
98
+ $text = $this->replace_url($text, 'profile_export_url', $url);
99
+
100
+ if (strpos($text, '{profile_form}') !== false) {
101
+ $text = str_replace('{profile_form}', $this->get_profile_form($user), $text);
102
+ }
103
+ return $text;
104
+ }
105
+
106
+ /**
107
+ *
108
+ * @param type $text
109
+ * @param type $key
110
+ * @param TNP_User $user
111
+ * @return string
112
+ */
113
+ function hook_newsletter_page_text($text, $key, $user) {
114
+ if ($key == 'profile') {
115
+ if (!$user || $user->status == TNP_User::STATUS_UNSUBSCRIBED) {
116
+ return 'Subscriber not found.';
117
+ }
118
+ $options = $this->get_options('main', $this->get_current_language($user));
119
+ return $options['text'];
120
+ }
121
+ return $text;
122
+ }
123
+
124
+ function shortcode_newsletter_profile($attrs, $content) {
125
+ $user = $this->check_user();
126
+
127
+ if (empty($user)) {
128
+ if (empty($content)) {
129
+ return __('Subscriber not found.', 'newsletter');
130
+ } else {
131
+ return $content;
132
+ }
133
+ }
134
+
135
+ return $this->get_profile_form($user);
136
+ }
137
+
138
+ function to_json($user) {
139
+ global $wpdb;
140
+
141
+
142
+ $fields = array('name', 'surname', 'sex', 'created', 'ip', 'email');
143
+ $data = array(
144
+ 'email' => $user->email,
145
+ 'name' => $user->name,
146
+ 'last_name' => $user->surname,
147
+ 'gender' => $user->sex,
148
+ 'created' => $user->created,
149
+ 'ip' => $user->ip,
150
+ );
151
+
152
+ // Lists
153
+ $data['lists'] = array();
154
+
155
+ $lists = $this->get_lists_public();
156
+ foreach ($lists as $list) {
157
+ $field = 'list_' . $list->id;
158
+ if ($user->$field == 1) {
159
+ $data['lists'][] = $list->name;
160
+ }
161
+ }
162
+
163
+ // Profile
164
+ $options_profile = get_option('newsletter_profile', array());
165
+ $data['profiles'] = array();
166
+ for ($i = 1; $i < NEWSLETTER_PROFILE_MAX; $i++) {
167
+ $field = 'profile_' . $i;
168
+ if ($options_profile[$field . '_status'] != 1 && $options_profile[$field . '_status'] != 2) {
169
+ continue;
170
+ }
171
+ $data['profiles'][] = array('name' => $options_profile[$field], 'value' => $user->$field);
172
+ }
173
+
174
+ // Newsletters
175
+ if ($this->options['export_newsletters']) {
176
+ $sent = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_sent where user_id=%d order by email_id asc", $user->id));
177
+ $newsletters = array();
178
+ foreach ($sent as $item) {
179
+ $action = 'none';
180
+ if ($item->open == 1) {
181
+ $action = 'read';
182
+ } else if ($item->open == 2) {
183
+ $action = 'click';
184
+ }
185
+
186
+ $email = $this->get_email($item->email_id);
187
+ if (!$email) {
188
+ continue;
189
+ }
190
+ // 'id'=>$item->email_id,
191
+ $newsletters[] = array('subject' => $email->subject, 'action' => $action, 'sent' => date('Y-m-d h:i:s', $email->send_on));
192
+ }
193
+
194
+ $data['newsletters'] = $newsletters;
195
+ }
196
+
197
+ $extra = apply_filters('newsletter_profile_export_extra', array());
198
+
199
+ $data = array_merge($extra, $data);
200
+
201
+ return json_encode($data, JSON_PRETTY_PRINT);
202
+ }
203
+
204
+ /**
205
+ * Build the profile editing form for the specified subscriber.
206
+ *
207
+ * @param TNP_User $user
208
+ * @return string
209
+ */
210
+ function get_profile_form($user) {
211
+ // Do not pay attention to option name here, it's a compatibility problem
212
+
213
+ $language = $this->get_user_language($user);
214
+ $options = NewsletterSubscription::instance()->get_options('profile', $language);
215
+
216
+ $buffer = '';
217
+
218
+ $buffer .= '<div class="tnp tnp-profile">';
219
+ $buffer .= '<form action="' . $this->build_action_url('ps') . '" method="post">';
220
+ $buffer .= '<input type="hidden" name="nk" value="' . esc_attr($user->id . '-' . $user->token) . '">';
221
+
222
+ $buffer .= '<div class="tnp-field tnp-field-email">';
223
+ $buffer .= '<label>' . esc_html($options['email']) . '</label>';
224
+ $buffer .= '<input class="tnp-email" type="text" name="ne" required value="' . esc_attr($user->email) . '">';
225
+ $buffer .= "</div>\n";
226
+
227
+
228
+ if ($options['name_status'] >= 1) {
229
+ $buffer .= '<div class="tnp-field tnp-field-firstname">';
230
+ $buffer .= '<label>' . esc_html($options['name']) . '</label>';
231
+ $buffer .= '<input class="tnp-firstname" type="text" name="nn" value="' . esc_attr($user->name) . '"' . ($options['name_rules'] == 1 ? ' required' : '') . '>';
232
+ $buffer .= "</div>\n";
233
+ }
234
+
235
+ if ($options['surname_status'] >= 1) {
236
+ $buffer .= '<div class="tnp-field tnp-field-lastname">';
237
+ $buffer .= '<label>' . esc_html($options['surname']) . '</label>';
238
+ $buffer .= '<input class="tnp-lastname" type="text" name="ns" value="' . esc_attr($user->surname) . '"' . ($options['surname_rules'] == 1 ? ' required' : '') . '>';
239
+ $buffer .= "</div>\n";
240
+ }
241
+
242
+ if ($options['sex_status'] >= 1) {
243
+ $buffer .= '<div class="tnp-field tnp-field-gender">';
244
+ $buffer .= '<label>' . esc_html($options['sex']) . '</label>';
245
+ $buffer .= '<select name="nx" class="tnp-gender"';
246
+ if ($options['sex_rules']) {
247
+ $buffer .= ' required ';
248
+ }
249
+ $buffer .= '>';
250
+ if ($options['sex_rules']) {
251
+ $buffer .= '<option value=""></option>';
252
+ }
253
+ $buffer .= '<option value="n"' . ($user->sex == 'n' ? ' selected' : '') . '>' . esc_html($options['sex_none']) . '</option>';
254
+ $buffer .= '<option value="f"' . ($user->sex == 'f' ? ' selected' : '') . '>' . esc_html($options['sex_female']) . '</option>';
255
+ $buffer .= '<option value="m"' . ($user->sex == 'm' ? ' selected' : '') . '>' . esc_html($options['sex_male']) . '</option>';
256
+ $buffer .= '</select>';
257
+ $buffer .= "</div>\n";
258
+ }
259
+
260
+ if ($this->is_multilanguage()) {
261
+
262
+ $languages = $this->get_languages();
263
+
264
+ $buffer .= '<div class="tnp-field tnp-field-language">';
265
+ $buffer .= '<label>' . __('Language', 'newsletter') . '</label>';
266
+ $buffer .= '<select name="nlng" class="tnp-language">';
267
+
268
+ $buffer .= '<option value="" disabled ' . ( empty($user->language) ? ' selected' : '' ) . '>' . __('Select language', 'newsletter') . '</option>';
269
+ foreach ($languages as $key => $l) {
270
+ $buffer .= '<option value="' . $key . '"' . ( $user->language == $key ? ' selected' : '' ) . '>' . esc_html($l) . '</option>';
271
+ }
272
+
273
+ $buffer .= '</select>';
274
+ $buffer .= "</div>\n";
275
+ }
276
+
277
+ // Profile
278
+ $profiles = NewsletterSubscription::instance()->get_profiles_for_profile($user->language);
279
+ foreach ($profiles as $profile) {
280
+ $i = $profile->id; // I'm lazy
281
+
282
+ $buffer .= '<div class="tnp-field tnp-field-profile">';
283
+ $buffer .= '<label>' . esc_html($profile->name) . '</label>';
284
+
285
+ $field = 'profile_' . $i;
286
+
287
+ if ($profile->is_text()) {
288
+ $buffer .= '<input class="tnp-profile tnp-profile-' . $i . '" type="text" name="np' . $i . '" value="' . esc_attr($user->$field) . '"' .
289
+ ($profile->is_required() ? ' required' : '') . '>';
290
+ }
291
+
292
+ if ($profile->is_select()) {
293
+ $buffer .= '<select class="tnp-profile tnp-profile-' . $i . '" name="np' . $i . '"' . ($profile->is_required() ? ' required' : '') . '>';
294
+ foreach ($profile->options as $option) {
295
+ $buffer .= '<option';
296
+ if ($option == $user->$field) {
297
+ $buffer .= ' selected';
298
+ }
299
+ $buffer .= '>' . esc_html($option) . '</option>';
300
+ }
301
+ $buffer .= '</select>';
302
+ }
303
+
304
+ $buffer .= "</div>\n";
305
+ }
306
+
307
+ // Lists
308
+ $lists = $this->get_lists_for_profile($language);
309
+ $tmp = '';
310
+ foreach ($lists as $list) {
311
+
312
+ $tmp .= '<div class="tnp-field tnp-field-list">';
313
+ $tmp .= '<label><input class="tnp-list tnp-list-' . $list->id . '" type="checkbox" name="nl[]" value="' . $list->id . '"';
314
+ $field = 'list_' . $list->id;
315
+ if ($user->$field == 1) {
316
+ $tmp .= ' checked';
317
+ }
318
+ $tmp .= '><span class="tnp-list-label">' . esc_html($list->name) . '</span></label>';
319
+ $tmp .= "</div>\n";
320
+ }
321
+
322
+ if (!empty($tmp)) {
323
+ $buffer .= '<div class="tnp-lists">' . "\n" . $tmp . "\n" . '</div>';
324
+ }
325
+
326
+ // Obsolete
327
+ $extra = apply_filters('newsletter_profile_extra', array(), $user);
328
+ foreach ($extra as $x) {
329
+ $buffer .= '<div class="tnp-field">';
330
+ $buffer .= '<label>' . $x['label'] . "</label>";
331
+ $buffer .= $x['field'];
332
+ $buffer .= "</div>\n";
333
+ }
334
+
335
+ $local_options = $this->get_options('', $language);
336
+
337
+ // Privacy
338
+ $privacy_url = NewsletterSubscription::instance()->get_privacy_url();
339
+ if (!empty($local_options['privacy_label']) && !empty($privacy_url)) {
340
+ $buffer .= '<div class="tnp-field tnp-field-privacy">';
341
+ if ($privacy_url) {
342
+ $buffer .= '<a href="' . $privacy_url . '" target="_blank">';
343
+ }
344
+
345
+ $buffer .= $local_options['privacy_label'];
346
+
347
+ if ($privacy_url) {
348
+ $buffer .= '</a>';
349
+ }
350
+ $buffer .= "</div>\n";
351
+ }
352
+
353
+ $buffer .= '<div class="tnp-field tnp-field-button">';
354
+ $buffer .= '<input class="tnp-submit" type="submit" value="' . esc_attr($local_options['save_label']) . '">';
355
+ $buffer .= "</div>\n";
356
+
357
+ $buffer .= "</form>\n</div>\n";
358
+
359
+ return $buffer;
360
+ }
361
+
362
+ /**
363
+ * Saves the subscriber data extracting them from the $_REQUEST and for the
364
+ * subscriber identified by the <code>$user</code> object.
365
+ *
366
+ * @return string|WP_Error If not an error the string represent the message to show
367
+ */
368
+ function save_profile($user) {
369
+ global $wpdb;
370
+
371
+ // Conatains the cleaned up user data to be saved
372
+ $data = array();
373
+ $data['id'] = $user->id;
374
+
375
+ $options = $this->get_options('', $this->get_current_language($user));
376
+ $options_profile = get_option('newsletter_profile', array());
377
+ $options_main = get_option('newsletter_main', array());
378
+
379
+ // Not an elegant interaction between modules but...
380
+ $subscription_module = NewsletterSubscription::instance();
381
+
382
+ require_once NEWSLETTER_INCLUDES_DIR . '/antispam.php';
383
+
384
+ $antispam = NewsletterAntispam::instance();
385
+
386
+ $email = $this->normalize_email(stripslashes($_REQUEST['ne']));
387
+
388
+ if ($antispam->is_address_blacklisted($email)) {
389
+ return new WP_Error('spam', 'That email address is not accepted');
390
+ }
391
+
392
+ if (!$email) {
393
+ return new WP_Error('email', $options['error']);
394
+ }
395
+
396
+ $email_changed = ($email != $user->email);
397
+
398
+ // If the email has been changed, check if it is available
399
+ if ($email_changed) {
400
+ $tmp = $this->get_user($email);
401
+ if ($tmp != null && $tmp->id != $user->id) {
402
+ return new WP_Error('inuse', $options['error']);
403
+ }
404
+ }
405
+
406
+ if ($email_changed && $subscription_module->is_double_optin()) {
407
+ set_transient('newsletter_user_' . $user->id . '_email', $email, DAY_IN_SECONDS);
408
+ } else {
409
+ $data['email'] = $email;
410
+ }
411
+
412
+ if (isset($_REQUEST['nn'])) {
413
+ $data['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
414
+ if ($antispam->is_spam_text($data['name'])) {
415
+ return new WP_Error('spam', 'That name/surname');
416
+ }
417
+ }
418
+ if (isset($_REQUEST['ns'])) {
419
+ $data['surname'] = $this->normalize_name(stripslashes($_REQUEST['ns']));
420
+ if ($antispam->is_spam_text($data['surname'])) {
421
+ return new WP_Error('spam', 'That name/surname');
422
+ }
423
+ }
424
+ if ($options_profile['sex_status'] >= 1) {
425
+ $data['sex'] = $_REQUEST['nx'][0];
426
+ // Wrong data injection check
427
+ if ($data['sex'] != 'm' && $data['sex'] != 'f' && $data['sex'] != 'n') {
428
+ die('Wrong sex field');
429
+ }
430
+ }
431
+ if (isset($_REQUEST['nlng'])) {
432
+ $languages = $this->get_languages();
433
+ if (isset($languages[$_REQUEST['nlng']])) {
434
+ $data['language'] = $_REQUEST['nlng'];
435
+ }
436
+ }
437
+
438
+ // Lists. If not list is present or there is no list to choose or all are unchecked.
439
+ $nl = array();
440
+ if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
441
+ $nl = $_REQUEST['nl'];
442
+ }
443
+
444
+ // Every possible list shown in the profile must be processed
445
+ $lists = $this->get_lists_for_profile();
446
+ foreach ($lists as $list) {
447
+ $field_name = 'list_' . $list->id;
448
+ $data[$field_name] = in_array($list->id, $nl) ? 1 : 0;
449
+ }
450
+
451
+ // Profile
452
+ $profiles = $this->get_profiles_public();
453
+ foreach ($profiles as $profile) {
454
+ if (isset($_REQUEST['np' . $profile->id])) {
455
+ $data['profile_' . $profile->id] = stripslashes($_REQUEST['np' . $profile->id]);
456
+ }
457
+ }
458
+
459
+ // Feed by Mail service is saved here
460
+ $data = apply_filters('newsletter_profile_save', $data);
461
+
462
+ if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
463
+ $data['status'] = TNP_User::STATUS_CONFIRMED;
464
+ }
465
+
466
+ $user = $this->save_user($data);
467
+ $this->add_user_log($user, 'profile');
468
+
469
+ // Send the activation again only if we use double opt-in, otherwise it has no meaning
470
+ if ($email_changed && $subscription_module->is_double_optin()) {
471
+ $user->email = $email;
472
+ $subscription_module->send_activation_email($user);
473
+ return $options['email_changed'];
474
+ }
475
+
476
+ return $options['saved'];
477
+ }
478
+
479
+ function admin_menu() {
480
+ $this->add_admin_page('index', 'Profile');
481
+ }
482
+
483
+ // Patch to avoid conflicts with the "newsletter_profile" option of the subscription module
484
+ // TODO: Fix it
485
+ public function get_prefix($sub = '', $language = '') {
486
+ if (empty($sub)) {
487
+ $sub = 'main';
488
+ }
489
+ return parent::get_prefix($sub, $language);
490
+ }
491
+
492
+ }
493
+
494
+ NewsletterProfile::instance();
readme.txt CHANGED
@@ -1,7 +1,7 @@
1
  === Newsletter - Send awesome emails from WordPress ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, lead generation, marketing automation
3
  Tested up to: 6.1
4
- Stable tag: 7.5.4
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -126,6 +126,10 @@ Thank you, The Newsletter Team
126
 
127
  == Changelog ==
128
 
 
 
 
 
129
  = 7.5.4 =
130
 
131
  * Fixed composer icons path for Amazon AWS+Bitnami installations
1
  === Newsletter - Send awesome emails from WordPress ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, lead generation, marketing automation
3
  Tested up to: 6.1
4
+ Stable tag: 7.5.5
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
126
 
127
  == Changelog ==
128
 
129
+ = 7.5.5 =
130
+
131
+ * Fixed access control on form manager base class
132
+
133
  = 7.5.4 =
134
 
135
  * Fixed composer icons path for Amazon AWS+Bitnami installations