profile.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. <?php
  2. defined('ABSPATH') || exit;
  3. require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
  4. class NewsletterProfile extends NewsletterModule {
  5. static $instance;
  6. /**
  7. * @return NewsletterProfile
  8. */
  9. static function instance() {
  10. if (self::$instance == null) {
  11. self::$instance = new NewsletterProfile();
  12. }
  13. return self::$instance;
  14. }
  15. function __construct() {
  16. parent::__construct('profile', '1.1.0');
  17. add_action('init', array($this, 'hook_init'), 1);
  18. add_action('wp_loaded', array($this, 'hook_wp_loaded'));
  19. add_shortcode('newsletter_profile', array($this, 'shortcode_newsletter_profile'));
  20. }
  21. function hook_init() {
  22. add_filter('newsletter_replace', array($this, 'hook_newsletter_replace'), 10, 3);
  23. add_filter('newsletter_page_text', array($this, 'hook_newsletter_page_text'), 10, 3);
  24. }
  25. function hook_wp_loaded() {
  26. global $wpdb;
  27. switch (Newsletter::instance()->action) {
  28. case 'profile':
  29. case 'p':
  30. case 'pe':
  31. $user = $this->check_user();
  32. if ($user == null) {
  33. die('No subscriber found.');
  34. }
  35. $profile_url = $this->build_message_url($this->options['url'], 'profile', $user);
  36. $profile_url = apply_filters('newsletter_profile_url', $profile_url, $user);
  37. wp_redirect($profile_url);
  38. die();
  39. break;
  40. case 'profile-save':
  41. case 'ps':
  42. $user = $this->save_profile();
  43. // $user->alert is a temporary field
  44. wp_redirect($this->build_message_url($this->options['url'], 'profile', $user, null, $user->alert));
  45. die();
  46. break;
  47. case 'profile_export':
  48. $user = $this->get_user_from_request(true);
  49. header('Content-Type: application/json;charset=UTF-8');
  50. echo $this->to_json($user);
  51. die();
  52. }
  53. }
  54. /**
  55. *
  56. * @param stdClass $user
  57. */
  58. function get_profile_export_url($user) {
  59. return $this->build_action_url('profile_export', $user);
  60. }
  61. /**
  62. *
  63. * @param stdClass $user
  64. */
  65. function get_profile_url($user) {
  66. return $this->build_action_url('profile', $user);
  67. }
  68. function hook_newsletter_replace($text, $user, $email) {
  69. if (!$user) {
  70. return $text;
  71. }
  72. // Profile edit page URL and link
  73. $url = $this->get_profile_url($user);
  74. $text = $this->replace_url($text, 'PROFILE_URL', $url);
  75. // Profile export URL and link
  76. $url = $this->get_profile_export_url($user);
  77. $text = $this->replace_url($text, 'PROFILE_EXPORT_URL', $url);
  78. if (strpos($text, '{profile_form}') !== false) {
  79. $text = str_replace('{profile_form}', $this->get_profile_form($user), $text);
  80. }
  81. return $text;
  82. }
  83. /**
  84. *
  85. * @param type $text
  86. * @param type $key
  87. * @param TNP_User $user
  88. * @return string
  89. */
  90. function hook_newsletter_page_text($text, $key, $user) {
  91. if ($key == 'profile') {
  92. if (!$user || $user->status == TNP_User::STATUS_UNSUBSCRIBED) {
  93. return 'Subscriber not found.';
  94. }
  95. $options = $this->get_options('main', $this->get_current_language($user));
  96. return $options['text'];
  97. }
  98. return $text;
  99. }
  100. function shortcode_newsletter_profile($attrs, $content) {
  101. $user = $this->check_user();
  102. if (empty($user)) {
  103. if (empty($content)) {
  104. return __('Subscriber not found.', 'newsletter');
  105. } else {
  106. return $content;
  107. }
  108. }
  109. return $this->get_profile_form($user);
  110. }
  111. function to_json($user) {
  112. global $wpdb;
  113. $fields = array('name', 'surname', 'sex', 'created', 'ip', 'email');
  114. $data = array(
  115. 'email'=>$user->email,
  116. 'name'=>$user->name,
  117. 'last_name'=>$user->surname,
  118. 'gender'=>$user->sex,
  119. 'created'=>$user->created,
  120. 'ip'=>$user->ip,
  121. );
  122. // Lists
  123. $data['lists'] = array();
  124. $lists = $this->get_lists_public();
  125. foreach ($lists as $list) {
  126. $field = 'list_' . $list->id;
  127. if ($user->$field == 1) {
  128. $data['lists'][] = $list->name;
  129. }
  130. }
  131. // Profile
  132. $options_profile = get_option('newsletter_profile', array());
  133. $data['profiles'] = array();
  134. for ($i=1; $i<NEWSLETTER_PROFILE_MAX; $i++) {
  135. $field = 'profile_' . $i;
  136. if ($options_profile[$field . '_status'] != 1 && $options_profile[$field . '_status'] != 2) {
  137. continue;
  138. }
  139. $data['profiles'][] = array('name' => $options_profile[$field], 'value' => $user->$field);
  140. }
  141. // Newsletters
  142. if ($this->options['export_newsletters']) {
  143. $sent = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_sent where user_id=%d order by email_id asc", $user->id));
  144. $newsletters = array();
  145. foreach ($sent as $item) {
  146. $action = 'none';
  147. if ($item->open == 1)
  148. $action = 'read';
  149. else if ($item->open == 2)
  150. $action = 'click';
  151. $email = $this->get_email($item->email_id);
  152. if (!$email)
  153. continue;
  154. // 'id'=>$item->email_id,
  155. $newsletters[] = array('subject' => $email->subject, 'action' => $action, 'sent' => date('Y-m-d h:i:s', $email->send_on));
  156. }
  157. $data['newsletters'] = $newsletters;
  158. }
  159. $extra = apply_filters('newsletter_profile_export_extra', array());
  160. $data = array_merge($extra, $data);
  161. return json_encode($data, JSON_PRETTY_PRINT);
  162. }
  163. function get_profile_form($user) {
  164. // Do not pay attention to option name here, it's a compatibility problem
  165. $language = $this->get_user_language($user);
  166. $options = NewsletterSubscription::instance()->get_options('profile', $language);
  167. $buffer = '';
  168. $buffer .= '<div class="tnp tnp-profile">';
  169. $buffer .= '<form action="' . $this->build_action_url('ps') . '" method="post" onsubmit="return newsletter_check(this)">';
  170. $buffer .= '<input type="hidden" name="nk" value="' . esc_attr($user->id . '-' . $user->token) . '">';
  171. $buffer .= '<div class="tnp-field tnp-field-email">';
  172. $buffer .= '<label>' . esc_html($options['email']) . '</label>';
  173. $buffer .= '<input class="tnp-email" type="text" name="ne" required value="' . esc_attr($user->email) . '">';
  174. $buffer .= "</div>\n";
  175. if ($options['name_status'] >= 1) {
  176. $buffer .= '<div class="tnp-field tnp-field-firstname">';
  177. $buffer .= '<label>' . esc_html($options['name']) . '</label>';
  178. $buffer .= '<input class="tnp-firstname" type="text" name="nn" value="' . esc_attr($user->name) . '"' . ($options['name_rules'] == 1 ? ' required' : '') . '>';
  179. $buffer .= "</div>\n";
  180. }
  181. if ($options['surname_status'] >= 1) {
  182. $buffer .= '<div class="tnp-field tnp-field-lastname">';
  183. $buffer .= '<label>' . esc_html($options['surname']) . '</label>';
  184. $buffer .= '<input class="tnp-lastname" type="text" name="ns" value="' . esc_attr($user->surname) . '"' . ($options['surname_rules'] == 1 ? ' required' : '') . '>';
  185. $buffer .= "</div>\n";
  186. }
  187. if ($options['sex_status'] >= 1) {
  188. $buffer .= '<div class="tnp-field tnp-field-gender">';
  189. $buffer .= '<label>' . esc_html($options['sex']) . '</label>';
  190. $buffer .= '<select name="nx" class="tnp-gender">';
  191. $buffer .= '<option value="f"' . ($user->sex == 'f' ? ' selected' : '') . '>' . esc_html($options['sex_female']) . '</option>';
  192. $buffer .= '<option value="m"' . ($user->sex == 'm' ? ' selected' : '') . '>' . esc_html($options['sex_male']) . '</option>';
  193. $buffer .= '<option value="n"' . ($user->sex == 'n' ? ' selected' : '') . '>' . esc_html($options['sex_none']) . '</option>';
  194. $buffer .= '</select>';
  195. $buffer .= "</div>\n";
  196. }
  197. // Profile
  198. for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
  199. if ($options['profile_' . $i . '_status'] == 0) {
  200. continue;
  201. }
  202. $buffer .= '<div class="tnp-field tnp-field-profile">';
  203. $buffer .= '<label>' . esc_html($options['profile_' . $i]) . '</label>';
  204. $field = 'profile_' . $i;
  205. if ($options['profile_' . $i . '_type'] == 'text') {
  206. $buffer .= '<input class="tnp-profile tnp-profile-' . $i . '" type="text" name="np' . $i . '" value="' . esc_attr($user->$field) . '"' .
  207. ($options['profile_' . $i . '_rules'] == 1 ? ' required' : '') . '>';
  208. }
  209. if ($options['profile_' . $i . '_type'] == 'select') {
  210. $buffer .= '<select class="tnp-profile tnp-profile-' . $i . '" name="np' . $i . '"' .
  211. ($options['profile_' . $i . '_rules'] == 1 ? ' required' : '') . '>';
  212. $opts = explode(',', $options['profile_' . $i . '_options']);
  213. for ($j = 0; $j < count($opts); $j++) {
  214. $opts[$j] = trim($opts[$j]);
  215. $buffer .= '<option';
  216. if ($opts[$j] == $user->$field)
  217. $buffer .= ' selected';
  218. $buffer .= '>' . esc_html($opts[$j]) . '</option>';
  219. }
  220. $buffer .= '</select>';
  221. }
  222. $buffer .= "</div>\n";
  223. }
  224. // Lists
  225. $lists = $this->get_lists_for_profile($language);
  226. $tmp = '';
  227. foreach ($lists as $list) {
  228. $tmp .= '<div class="tnp-field tnp-field-list">';
  229. $tmp .= '<label><input class="tnp-list tnp-list-' . $list->id . '" type="checkbox" name="nl[]" value="' . $list->id . '"';
  230. $field = 'list_' . $list->id;
  231. if ($user->$field == 1) {
  232. $tmp .= ' checked';
  233. }
  234. $tmp .= '><span class="tnp-list-label">' . esc_html($list->name) . '</span></label>';
  235. $tmp .= "</div>\n";
  236. }
  237. if (!empty($tmp)) {
  238. $buffer .= '<div class="tnp-lists">' . "\n" . $tmp . "\n" . '</div>';
  239. }
  240. $extra = apply_filters('newsletter_profile_extra', array(), $user);
  241. foreach ($extra as $x) {
  242. $buffer .= '<div class="tnp-field">';
  243. $buffer .= '<label>' . $x['label'] . "</label>";
  244. $buffer .= $x['field'];
  245. $buffer .= "</div>\n";
  246. }
  247. $local_options = $this->get_options('', $this->get_user_language($user));
  248. // Privacy
  249. $privacy_url = NewsletterSubscription::instance()->get_privacy_url();
  250. if (!empty($local_options['privacy_label']) && !empty($privacy_url)) {
  251. $buffer .= '<div class="tnp-field tnp-field-privacy">';
  252. if ($privacy_url) {
  253. $buffer .= '<a href="' . $privacy_url . '" target="_blank">';
  254. }
  255. $buffer .= $local_options['privacy_label'];
  256. if ($privacy_url) {
  257. $buffer .= '</a>';
  258. }
  259. $buffer .= "</div>\n";
  260. }
  261. $buffer .= '<div class="tnp-field tnp-field-button">';
  262. $buffer .= '<input class="tnp-submit" type="submit" value="' . esc_attr($local_options['save_label']) . '">';
  263. $buffer .= "</div>\n";
  264. $buffer .= "</form>\n</div>\n";
  265. return $buffer;
  266. }
  267. /**
  268. * Saves the subscriber data.
  269. *
  270. * @return type
  271. */
  272. function save_profile() {
  273. global $wpdb;
  274. // Get the current subscriber, fail if not found
  275. $user = $this->get_user_from_request(true);
  276. // Conatains the cleaned up user data to be saved
  277. $data = array();
  278. $data['id'] = $user->id;
  279. $options_profile = get_option('newsletter_profile', array());
  280. $options_main = get_option('newsletter_main', array());
  281. // Not an elegant interaction between modules but...
  282. $subscription_module = NewsletterSubscription::instance();
  283. if (!$this->is_email($_REQUEST['ne'])) {
  284. $user->alert = $this->options['profile_error'];
  285. return $user;
  286. }
  287. $email = $this->normalize_email(stripslashes($_REQUEST['ne']));
  288. $email_changed = ($email != $user->email);
  289. // If the email has been changed, check if it is available
  290. if ($email_changed) {
  291. $tmp = $this->get_user($email);
  292. if ($tmp != null && $tmp->id != $user->id) {
  293. // TODO: Move the label on profile setting panel
  294. $user->alert = $this->options['error'];
  295. return $user;
  296. }
  297. $data['status'] = Newsletter::STATUS_NOT_CONFIRMED;
  298. }
  299. // General data
  300. $data['email'] = $email;
  301. if (isset($_REQUEST['nn'])) {
  302. $data['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
  303. }
  304. if (isset($_REQUEST['ns'])) {
  305. $data['surname'] = $this->normalize_name(stripslashes($_REQUEST['ns']));
  306. }
  307. if ($options_profile['sex_status'] >= 1) {
  308. $data['sex'] = $_REQUEST['nx'][0];
  309. // Wrong data injection check
  310. if ($data['sex'] != 'm' && $data['sex'] != 'f' && $data['sex'] != 'n') {
  311. die('Wrong sex field');
  312. }
  313. }
  314. // Lists. If not list is present or there is no list to choose or all are unchecked.
  315. $nl = array();
  316. if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
  317. $nl = $_REQUEST['nl'];
  318. }
  319. // Every possible list shown in the profile must be processed
  320. $lists = $this->get_lists_for_profile();
  321. foreach ($lists as $list) {
  322. $field_name = 'list_' . $list->id;
  323. $data[$field_name] = in_array($list->id, $nl) ? 1 : 0;
  324. }
  325. // Profile
  326. for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
  327. // Private fields cannot be changed by the subscriber
  328. if ($options_profile['profile_' . $i . '_status'] == 0) {
  329. continue;
  330. }
  331. $data['profile_' . $i] = stripslashes($_REQUEST['np' . $i]);
  332. }
  333. // Feed by Mail service is saved here
  334. $data = apply_filters('newsletter_profile_save', $data);
  335. if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
  336. $data['status'] = TNP_User::STATUS_CONFIRMED;
  337. }
  338. $user = $this->save_user($data);
  339. $this->add_user_log($user, 'profile');
  340. // Send the activation again only if we use double opt-in, otherwise it has no meaning
  341. // TODO: Maybe define a specific email for that and not the activation email
  342. if ($email_changed && $subscription_module->is_double_optin()) {
  343. $subscription_module->send_activation_email($user);
  344. // TODO: Move this option on new profile configuration panel
  345. $alert = $this->options['profile_email_changed'];
  346. }
  347. if (isset($alert)) {
  348. $user->alert = $alert;
  349. } else {
  350. // TODO: Move this label on profile settings panel
  351. $user->alert = $this->options['saved'];
  352. }
  353. return $user;
  354. }
  355. function upgrade() {
  356. global $wpdb, $charset_collate;
  357. parent::upgrade();
  358. // Migration code
  359. if (empty($this->options) || empty($this->options['email_changed'])) {
  360. // Options of the subscription module (worng name, I know)
  361. $options = get_option('newsletter');
  362. $this->options['saved'] = $options['profile_saved'];
  363. $this->options['text'] = $options['profile_text'];
  364. $this->options['email_changed'] = $options['profile_email_changed'];
  365. $this->options['error'] = $options['profile_error'];
  366. $this->options['url'] = $options['profile_url'];
  367. $this->save_options($this->options);
  368. }
  369. if (empty($this->options) || empty($this->options['save_label'])) {
  370. $options = get_option('newsletter_profile');
  371. $this->options['save_label'] = $options['save'];
  372. $this->save_options($this->options);
  373. }
  374. }
  375. function admin_menu() {
  376. $this->add_admin_page('index', 'Profile');
  377. }
  378. // Patch to avoid conflicts with the "newsletter_profile" option of the subscription module
  379. // TODO: Fix it
  380. public function get_prefix($sub = '', $language='') {
  381. if (empty($sub)) {
  382. $sub = 'main';
  383. }
  384. return parent::get_prefix($sub, $language);
  385. }
  386. }
  387. NewsletterProfile::instance();