first commit

This commit is contained in:
2024-07-15 11:28:08 +02:00
commit f52d538ea5
21891 changed files with 6161164 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
<?php
$options = array();
// Profile page
$options['text'] = '{profile_form}' . __('
<p>If you change your email address, a confirmation email will be sent to activate it.</p>
<p><a href="{unsubscription_confirm_url}">Cancel your subscription</a></p>', 'newsletter');
// Profile page messages
$options['email_changed'] = __("Your email has been changed, an activation email has been sent with instructions.", 'newsletter');
$options['error'] = __("Your email is not valid or already in use.", 'newsletter');
$options['save_label'] = __('Save', 'newsletter');
$options['privacy_label'] = __('Read our privacy note', 'newsletter');
$options['saved'] = __('Profile saved.', 'newsletter');
$options['export_newsletters'] = 0;
$options['url'] = '';

View File

@@ -0,0 +1,164 @@
<?php
defined('ABSPATH') || exit;
require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
$controls = new NewsletterControls();
$module = NewsletterProfile::instance();
$current_language = $module->get_current_language();
$is_all_languages = $module->is_all_languages();
if (!$is_all_languages) {
$controls->warnings[] = 'You are configuring the language "<strong>' . $current_language . '</strong>". Switch to "all languages" to see every options.';
}
// Profile options are still inside the main options
if ($controls->is_action()) {
if ($controls->is_action('save')) {
$module->save_options($controls->data, '', null, $current_language);
$controls->add_message_saved();
}
if ($controls->is_action('reset')) {
$module->reset_options();
$controls->data = $module->get_options('', $current_language);
$controls->add_message_reset();
}
} else {
$controls->data = $module->get_options('', $current_language);
}
?>
<div class="wrap tnp-profile tnp-profile-index" id="tnp-wrap">
<?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
<div id="tnp-heading">
<h2><?php _e('The subscriber profile page', 'newsletter') ?></h2>
<?php $controls->page_help('https://www.thenewsletterplugin.com/documentation/profile-page') ?>
</div>
<div id="tnp-body">
<form id="channel" method="post" action="">
<?php $controls->init(); ?>
<div id="tabs">
<ul>
<li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
<li><a href="#tabs-export"><?php _e('Subscriber data export', 'newsletter') ?></a></li>
</ul>
<div id="tabs-general">
<table class="form-table">
<tr>
<th><?php _e('Profile page', 'newsletter') ?>
<br><?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscription#profile') ?>
</th>
<td>
<?php $controls->wp_editor('text'); ?>
</td>
</tr>
<tr>
<th><?php _e('Alternative profile page URL', 'newsletter') ?></th>
<td>
<?php $controls->text('url', 70); ?>
</td>
</tr>
</table>
<h3><?php _e('Messages', 'newsletter') ?></h3>
<table class="form-table">
<tr>
<th><?php _e('Profile saved', 'newsletter') ?></th>
<td>
<?php $controls->text('saved', 80); ?>
</td>
</tr>
<tr>
<tr>
<th><?php _e('Email changed alert', 'newsletter') ?></th>
<td>
<?php $controls->text('email_changed', 80); ?>
</td>
</tr>
<tr>
<tr>
<tr>
<th><?php _e('General error', 'newsletter') ?></th>
<td>
<?php $controls->text('error', 80); ?>
</td>
</tr>
</table>
<h3><?php _e('Labels', 'newsletter') ?></h3>
<table class="form-table">
<tr>
<th><?php _e('"Save" label', 'newsletter') ?></th>
<td>
<?php $controls->text('save_label'); ?>
</td>
</tr>
<tr>
<th><?php _e('Privacy link text', 'newsletter') ?></th>
<td>
<?php $controls->text('privacy_label', 80); ?>
<p class="description">
</p>
</td>
</tr>
</table>
</div>
<div id="tabs-export">
<?php if ($is_all_languages) { ?>
<table class="form-table">
<tr>
<th>
<?php _e('Log of sent newsletters', 'newsletter') ?>
</th>
<td>
<?php $controls->yesno('export_newsletters'); ?>
</td>
</tr>
</table>
<?php } else { ?>
<p>Switch to "All languages" to manage these options.</p>
<?php } ?>
</div>
</div>
<p>
<?php $controls->button_save() ?>
<?php $controls->button_reset() ?>
</p>
</form>
</div>
<?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
</div>

View File

@@ -0,0 +1,473 @@
<?php
defined('ABSPATH') || exit;
require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
class NewsletterProfile extends NewsletterModule {
static $instance;
/**
* @return NewsletterProfile
*/
static function instance() {
if (self::$instance == null) {
self::$instance = new NewsletterProfile();
}
return self::$instance;
}
function __construct() {
parent::__construct('profile', '1.1.0');
add_action('init', array($this, 'hook_init'), 1);
add_action('wp_loaded', array($this, 'hook_wp_loaded'));
add_shortcode('newsletter_profile', array($this, 'shortcode_newsletter_profile'));
}
function hook_init() {
add_filter('newsletter_replace', array($this, 'hook_newsletter_replace'), 10, 3);
add_filter('newsletter_page_text', array($this, 'hook_newsletter_page_text'), 10, 3);
}
function hook_wp_loaded() {
global $wpdb;
switch (Newsletter::instance()->action) {
case 'profile':
case 'p':
case 'pe':
$user = $this->check_user();
if ($user == null) {
die('No subscriber found.');
}
$profile_url = $this->build_message_url($this->options['url'], 'profile', $user);
$profile_url = apply_filters('newsletter_profile_url', $profile_url, $user);
wp_redirect($profile_url);
die();
break;
case 'profile-save':
case 'ps':
$user = $this->save_profile();
// $user->alert is a temporary field
wp_redirect($this->build_message_url($this->options['url'], 'profile', $user, null, $user->alert));
die();
break;
case 'profile_export':
$user = $this->get_user_from_request(true);
header('Content-Type: application/json;charset=UTF-8');
echo $this->to_json($user);
die();
}
}
/**
*
* @param stdClass $user
*/
function get_profile_export_url($user) {
return $this->build_action_url('profile_export', $user);
}
/**
*
* @param stdClass $user
*/
function get_profile_url($user) {
return $this->build_action_url('profile', $user);
}
function hook_newsletter_replace($text, $user, $email) {
if (!$user) {
return $text;
}
// Profile edit page URL and link
$url = $this->get_profile_url($user);
$text = $this->replace_url($text, 'PROFILE_URL', $url);
// Profile export URL and link
$url = $this->get_profile_export_url($user);
$text = $this->replace_url($text, 'PROFILE_EXPORT_URL', $url);
if (strpos($text, '{profile_form}') !== false) {
$text = str_replace('{profile_form}', $this->get_profile_form($user), $text);
}
return $text;
}
/**
*
* @param type $text
* @param type $key
* @param TNP_User $user
* @return string
*/
function hook_newsletter_page_text($text, $key, $user) {
if ($key == 'profile') {
if (!$user || $user->status == TNP_User::STATUS_UNSUBSCRIBED) {
return 'Subscriber not found.';
}
$options = $this->get_options('main', $this->get_current_language($user));
return $options['text'];
}
return $text;
}
function shortcode_newsletter_profile($attrs, $content) {
$user = $this->check_user();
if (empty($user)) {
if (empty($content)) {
return __('Subscriber not found.', 'newsletter');
} else {
return $content;
}
}
return $this->get_profile_form($user);
}
function to_json($user) {
global $wpdb;
$fields = array('name', 'surname', 'sex', 'created', 'ip', 'email');
$data = array(
'email'=>$user->email,
'name'=>$user->name,
'last_name'=>$user->surname,
'gender'=>$user->sex,
'created'=>$user->created,
'ip'=>$user->ip,
);
// Lists
$data['lists'] = array();
$lists = $this->get_lists_public();
foreach ($lists as $list) {
$field = 'list_' . $list->id;
if ($user->$field == 1) {
$data['lists'][] = $list->name;
}
}
// Profile
$options_profile = get_option('newsletter_profile', array());
$data['profiles'] = array();
for ($i=1; $i<NEWSLETTER_PROFILE_MAX; $i++) {
$field = 'profile_' . $i;
if ($options_profile[$field . '_status'] != 1 && $options_profile[$field . '_status'] != 2) {
continue;
}
$data['profiles'][] = array('name' => $options_profile[$field], 'value' => $user->$field);
}
// Newsletters
if ($this->options['export_newsletters']) {
$sent = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_sent where user_id=%d order by email_id asc", $user->id));
$newsletters = array();
foreach ($sent as $item) {
$action = 'none';
if ($item->open == 1)
$action = 'read';
else if ($item->open == 2)
$action = 'click';
$email = $this->get_email($item->email_id);
if (!$email)
continue;
// 'id'=>$item->email_id,
$newsletters[] = array('subject' => $email->subject, 'action' => $action, 'sent' => date('Y-m-d h:i:s', $email->send_on));
}
$data['newsletters'] = $newsletters;
}
$extra = apply_filters('newsletter_profile_export_extra', array());
$data = array_merge($extra, $data);
return json_encode($data, JSON_PRETTY_PRINT);
}
function get_profile_form($user) {
// Do not pay attention to option name here, it's a compatibility problem
$language = $this->get_user_language($user);
$options = NewsletterSubscription::instance()->get_options('profile', $language);
$buffer = '';
$buffer .= '<div class="tnp tnp-profile">';
$buffer .= '<form action="' . $this->build_action_url('ps') . '" method="post" onsubmit="return newsletter_check(this)">';
$buffer .= '<input type="hidden" name="nk" value="' . esc_attr($user->id . '-' . $user->token) . '">';
$buffer .= '<div class="tnp-field tnp-field-email">';
$buffer .= '<label>' . esc_html($options['email']) . '</label>';
$buffer .= '<input class="tnp-email" type="text" name="ne" required value="' . esc_attr($user->email) . '">';
$buffer .= "</div>\n";
if ($options['name_status'] >= 1) {
$buffer .= '<div class="tnp-field tnp-field-firstname">';
$buffer .= '<label>' . esc_html($options['name']) . '</label>';
$buffer .= '<input class="tnp-firstname" type="text" name="nn" value="' . esc_attr($user->name) . '"' . ($options['name_rules'] == 1 ? ' required' : '') . '>';
$buffer .= "</div>\n";
}
if ($options['surname_status'] >= 1) {
$buffer .= '<div class="tnp-field tnp-field-lastname">';
$buffer .= '<label>' . esc_html($options['surname']) . '</label>';
$buffer .= '<input class="tnp-lastname" type="text" name="ns" value="' . esc_attr($user->surname) . '"' . ($options['surname_rules'] == 1 ? ' required' : '') . '>';
$buffer .= "</div>\n";
}
if ($options['sex_status'] >= 1) {
$buffer .= '<div class="tnp-field tnp-field-gender">';
$buffer .= '<label>' . esc_html($options['sex']) . '</label>';
$buffer .= '<select name="nx" class="tnp-gender">';
$buffer .= '<option value="f"' . ($user->sex == 'f' ? ' selected' : '') . '>' . esc_html($options['sex_female']) . '</option>';
$buffer .= '<option value="m"' . ($user->sex == 'm' ? ' selected' : '') . '>' . esc_html($options['sex_male']) . '</option>';
$buffer .= '<option value="n"' . ($user->sex == 'n' ? ' selected' : '') . '>' . esc_html($options['sex_none']) . '</option>';
$buffer .= '</select>';
$buffer .= "</div>\n";
}
// Profile
for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
if ($options['profile_' . $i . '_status'] == 0) {
continue;
}
$buffer .= '<div class="tnp-field tnp-field-profile">';
$buffer .= '<label>' . esc_html($options['profile_' . $i]) . '</label>';
$field = 'profile_' . $i;
if ($options['profile_' . $i . '_type'] == 'text') {
$buffer .= '<input class="tnp-profile tnp-profile-' . $i . '" type="text" name="np' . $i . '" value="' . esc_attr($user->$field) . '"' .
($options['profile_' . $i . '_rules'] == 1 ? ' required' : '') . '>';
}
if ($options['profile_' . $i . '_type'] == 'select') {
$buffer .= '<select class="tnp-profile tnp-profile-' . $i . '" name="np' . $i . '"' .
($options['profile_' . $i . '_rules'] == 1 ? ' required' : '') . '>';
$opts = explode(',', $options['profile_' . $i . '_options']);
for ($j = 0; $j < count($opts); $j++) {
$opts[$j] = trim($opts[$j]);
$buffer .= '<option';
if ($opts[$j] == $user->$field)
$buffer .= ' selected';
$buffer .= '>' . esc_html($opts[$j]) . '</option>';
}
$buffer .= '</select>';
}
$buffer .= "</div>\n";
}
// Lists
$lists = $this->get_lists_for_profile($language);
$tmp = '';
foreach ($lists as $list) {
$tmp .= '<div class="tnp-field tnp-field-list">';
$tmp .= '<label><input class="tnp-list tnp-list-' . $list->id . '" type="checkbox" name="nl[]" value="' . $list->id . '"';
$field = 'list_' . $list->id;
if ($user->$field == 1) {
$tmp .= ' checked';
}
$tmp .= '><span class="tnp-list-label">' . esc_html($list->name) . '</span></label>';
$tmp .= "</div>\n";
}
if (!empty($tmp)) {
$buffer .= '<div class="tnp-lists">' . "\n" . $tmp . "\n" . '</div>';
}
$extra = apply_filters('newsletter_profile_extra', array(), $user);
foreach ($extra as $x) {
$buffer .= '<div class="tnp-field">';
$buffer .= '<label>' . $x['label'] . "</label>";
$buffer .= $x['field'];
$buffer .= "</div>\n";
}
$local_options = $this->get_options('', $this->get_user_language($user));
// Privacy
$privacy_url = NewsletterSubscription::instance()->get_privacy_url();
if (!empty($local_options['privacy_label']) && !empty($privacy_url)) {
$buffer .= '<div class="tnp-field tnp-field-privacy">';
if ($privacy_url) {
$buffer .= '<a href="' . $privacy_url . '" target="_blank">';
}
$buffer .= $local_options['privacy_label'];
if ($privacy_url) {
$buffer .= '</a>';
}
$buffer .= "</div>\n";
}
$buffer .= '<div class="tnp-field tnp-field-button">';
$buffer .= '<input class="tnp-submit" type="submit" value="' . esc_attr($local_options['save_label']) . '">';
$buffer .= "</div>\n";
$buffer .= "</form>\n</div>\n";
return $buffer;
}
/**
* Saves the subscriber data.
*
* @return type
*/
function save_profile() {
global $wpdb;
// Get the current subscriber, fail if not found
$user = $this->get_user_from_request(true);
// Conatains the cleaned up user data to be saved
$data = array();
$data['id'] = $user->id;
$options_profile = get_option('newsletter_profile', array());
$options_main = get_option('newsletter_main', array());
// Not an elegant interaction between modules but...
$subscription_module = NewsletterSubscription::instance();
if (!$this->is_email($_REQUEST['ne'])) {
$user->alert = $this->options['profile_error'];
return $user;
}
$email = $this->normalize_email(stripslashes($_REQUEST['ne']));
$email_changed = ($email != $user->email);
// If the email has been changed, check if it is available
if ($email_changed) {
$tmp = $this->get_user($email);
if ($tmp != null && $tmp->id != $user->id) {
// TODO: Move the label on profile setting panel
$user->alert = $this->options['error'];
return $user;
}
$data['status'] = Newsletter::STATUS_NOT_CONFIRMED;
}
// General data
$data['email'] = $email;
if (isset($_REQUEST['nn'])) {
$data['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
}
if (isset($_REQUEST['ns'])) {
$data['surname'] = $this->normalize_name(stripslashes($_REQUEST['ns']));
}
if ($options_profile['sex_status'] >= 1) {
$data['sex'] = $_REQUEST['nx'][0];
// Wrong data injection check
if ($data['sex'] != 'm' && $data['sex'] != 'f' && $data['sex'] != 'n') {
die('Wrong sex field');
}
}
// Lists. If not list is present or there is no list to choose or all are unchecked.
$nl = array();
if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
$nl = $_REQUEST['nl'];
}
// Every possible list shown in the profile must be processed
$lists = $this->get_lists_for_profile();
foreach ($lists as $list) {
$field_name = 'list_' . $list->id;
$data[$field_name] = in_array($list->id, $nl) ? 1 : 0;
}
// Profile
for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
// Private fields cannot be changed by the subscriber
if ($options_profile['profile_' . $i . '_status'] == 0) {
continue;
}
$data['profile_' . $i] = stripslashes($_REQUEST['np' . $i]);
}
// Feed by Mail service is saved here
$data = apply_filters('newsletter_profile_save', $data);
if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
$data['status'] = TNP_User::STATUS_CONFIRMED;
}
$user = $this->save_user($data);
$this->add_user_log($user, 'profile');
// Send the activation again only if we use double opt-in, otherwise it has no meaning
// TODO: Maybe define a specific email for that and not the activation email
if ($email_changed && $subscription_module->is_double_optin()) {
$subscription_module->send_activation_email($user);
// TODO: Move this option on new profile configuration panel
$alert = $this->options['profile_email_changed'];
}
if (isset($alert)) {
$user->alert = $alert;
} else {
// TODO: Move this label on profile settings panel
$user->alert = $this->options['saved'];
}
return $user;
}
function upgrade() {
global $wpdb, $charset_collate;
parent::upgrade();
// Migration code
if (empty($this->options) || empty($this->options['email_changed'])) {
// Options of the subscription module (worng name, I know)
$options = get_option('newsletter');
$this->options['saved'] = $options['profile_saved'];
$this->options['text'] = $options['profile_text'];
$this->options['email_changed'] = $options['profile_email_changed'];
$this->options['error'] = $options['profile_error'];
$this->options['url'] = $options['profile_url'];
$this->save_options($this->options);
}
if (empty($this->options) || empty($this->options['save_label'])) {
$options = get_option('newsletter_profile');
$this->options['save_label'] = $options['save'];
$this->save_options($this->options);
}
}
function admin_menu() {
$this->add_admin_page('index', 'Profile');
}
// Patch to avoid conflicts with the "newsletter_profile" option of the subscription module
// TODO: Fix it
public function get_prefix($sub = '', $language='') {
if (empty($sub)) {
$sub = 'main';
}
return parent::get_prefix($sub, $language);
}
}
NewsletterProfile::instance();