Current Path : /home/ncdcgo/public_html/wp-content/plugins backup/newsletter-automated/ |
Current File : /home/ncdcgo/public_html/wp-content/plugins backup/newsletter-automated/plugin.php |
<?php class NewsletterAutomated extends NewsletterAddon { const THEME_TYPE_CLASSIC = 0; const THEME_TYPE_COMPOSER = 1; static $instance; function __construct($version) { self::$instance = $this; parent::__construct('automated', $version); $this->setup_options(); // Here since the alternative wp cron runs tasks on init! add_action('newsletter_automated', array($this, 'hook_newsletter_automated')); add_action('newsletter_action', array($this, 'hook_newsletter_action')); } function upgrade($first_time = false) { global $wpdb, $charset_collate; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta("CREATE TABLE `" . $wpdb->prefix . "newsletter_automated` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `data` longtext, `theme` longtext, `theme_type` int default 0, `last_run` int default 0, `email_id` int default 0, `last_run_status` int default 0, PRIMARY KEY (`id`)) $charset_collate;"); maybe_convert_table_to_utf8mb4($wpdb->prefix . 'newsletter_automated'); if ($this->version > '4.4.0') { $channels = $this->get_channels(); foreach ($channels as $channel) { $last_run = (int) get_option('newsletter_automated_last_run_' . $channel->id); if ($last_run) { $this->query($wpdb->prepare("update {$wpdb->prefix}newsletter_automated set last_run=%d where id=%d limit 1", $last_run, $channel->id)); } delete_option('newsletter_automated_last_run_' . $channel->id); } } } function init() { if (is_admin()) { if (Newsletter::instance()->is_allowed()) { add_action('admin_menu', array($this, 'hook_admin_menu'), 100); add_filter('newsletter_menu_newsletters', array($this, 'hook_newsletter_menu_newsletters')); } add_action('admin_enqueue_scripts', array($this, 'hook_admin_enqueue_scripts')); add_filter('newsletter_lists_notes', array($this, 'hook_newsletter_lists_notes'), 10, 2); } } function hook_newsletter_action($action) { switch ($action) { case 'automated-preview': $newsletter = Newsletter::instance(); if (!$newsletter->is_allowed()) { die('Not enough privileges'); } include __DIR__ . '/preview.php'; die(); break; } } function hook_newsletter_lists_notes($notes, $list_id) { $channels = $this->get_channels(); foreach ($channels as $channel) { if ($channel->data['list'] == $list_id) { $notes[] = 'Linked to automated channel "' . $channel->data['name'] . '"'; } } return $notes; } function hook_admin_enqueue_scripts() { wp_enqueue_script('jquery-ui-accordion'); } function hook_newsletter_menu_newsletters($entries) { $entries[] = array('label' => '<i class="fas fa-calendar-alt"></i> Automated Newsletters', 'url' => '?page=newsletter_automated_index', 'description' => 'Automatically generated from your blog content'); return $entries; } function is_scheduled_day($channel, $time) { // 1 - Monday, ..., 7 - Sunday $day_of_week = gmdate('N', $time + get_option('gmt_offset') * 3600); $day_of_month = gmdate('j', $time + get_option('gmt_offset') * 3600); $week_of_month = floor(($day_of_month - 1) / 7) + 1; $options = $channel->data; if ($options['frequency'] == 'monthly') { if ($day_of_month < 8) { if (!isset($options['monthly_1_days']) || !is_array($options['monthly_1_days'])) { return false; } if (array_search($day_of_week, $options['monthly_1_days']) === false) { return false; } } else if ($day_of_month < 15) { if (!isset($options['monthly_2_days']) || !is_array($options['monthly_2_days'])) { return false; } if (array_search($day_of_week, $options['monthly_2_days']) === false) { return false; } } else if ($day_of_month < 22) { if (!isset($options['monthly_3_days']) || !is_array($options['monthly_3_days'])) { return false; } if (array_search($day_of_week, $options['monthly_3_days']) === false) { return false; } } else if ($day_of_month < 29) { if (!isset($options['monthly_4_days']) || !is_array($options['monthly_4_days'])) { return false; } if (array_search($day_of_week, $options['monthly_4_days']) === false) { return false; } } else { if (!isset($options['monthly_5_days']) || !is_array($options['monthly_5_days'])) { return false; } if (array_search($day_of_week, $options['monthly_5_days']) === false) { return false; } } } else { if ($options['day_' . $day_of_week] == 0) { return false; } } return true; } /** * Called daily to generate the email (if needed). Return false is not generated * when called directly. Called daily for each channel at the channel delivery time. */ function hook_newsletter_automated($id = null, $force = false) { global $wpdb; $newsletter = Newsletter::instance(); $logger = $this->get_logger(); $logger->info('Waking up channel ' . $id); $channel = $this->get_channel($id); if (!$channel) { $logger->fatal('Channel "' . $id . '" does not exist. Could be WP has an old schduled job with a wrong channel ID. That is not actually a real problem.'); return false; } $last_wakeup = get_option('newsletter_automated_' . $id . '_wakeup', 0); $logger->debug('Last wakeup: ' . $last_wakeup); if ($last_wakeup > time() - 900) { $logger->fatal('Channel ' . $id . ' waked up too early. It means the job has been executed twice. We managed it but there could be a problem on WP scheduler.'); return false; } update_option('newsletter_automated_' . $id . '_wakeup', time(), false); if (!isset($channel->data['enabled']) || $channel->data['enabled'] != 1) { $logger->info('Channel ' . $id . ' not enabled. It should have not been waked up.'); return false; } if (!$force && !$this->is_scheduled_day($channel, time())) { $logger->info('Channel ' . $id . ' not scheduled for today.'); return false; } if (!$force && !is_user_logged_in()) { if (!empty($this->options['user_id'])) { wp_set_current_user($this->options['user_id']); $logger->info('Current user set to ' . $this->options['user_id']); } } $email = $this->create_email($channel, $channel->last_run); if (!$force) { if (!empty($this->options['user_id'])) { wp_set_current_user(0); } } if (!$email) { $logger->info('Email not created: the template or the theme returned to skip this delivery.'); $this->query($wpdb->prepare("update {$wpdb->prefix}newsletter_automated set last_run_status=1 where id=%d limit 1", $id)); return false; } $logger->info('Email generated with subject: ' . $email['subject']); $email['type'] = 'automated_' . $id; $email['total'] = $this->get_subscriber_count($channel); $email['query'] = $this->generate_subscribers_list_query($channel); $newsletter->save_email($email); // Used as timestamp for the next posts extraction $this->set_last_run($id, time()); $this->query($wpdb->prepare("update {$wpdb->prefix}newsletter_automated set last_run_status=0 where id=%d limit 1", $id)); return true; } function hook_admin_menu() { add_submenu_page('newsletter_main_index', 'Automated Newsletter', '<span class="tnp-side-menu">Automated</span>', 'exist', 'newsletter_automated_index', array($this, 'menu_page_index')); add_submenu_page(null, 'Edit', 'Edit', 'exist', 'newsletter-automated/edit.php'); add_submenu_page(null, 'Newsletters', 'Newsletters', 'exist', 'newsletter_automated_newsletters', array($this, 'menu_page_newsletters')); add_submenu_page(null, 'Config', 'Config', 'exist', 'newsletter_automated_config', function () { require __DIR__ . '/config.php'; }); add_submenu_page(null, 'Template', 'Template', 'exist', 'newsletter_automated_template', function () { require __DIR__ . '/template.php'; }); } function menu_page_index() { require __DIR__ . '/index.php'; } function menu_page_newsletters() { require __DIR__ . '/newsletters.php'; } function hook_strip_shortcodes_tagnames($tags) { $tags[] = 'vc_row'; $tags[] = 'vc_column'; $tags[] = 'vc_column_text'; return $tags; } /** * Extract all post based on options (passed or the ones saved). * Sets some variables inside $newsletter, for compatibility with old themes. * * @param array $options */ function get_posts($options = null) { global $newsletter; $cat = ''; // Channels have positive category selection if (!empty($options['categories'])) { $cat = implode(',', $options['categories']); } if (!isset($options['max_posts'])) $options['max_posts'] = 10; // Extract the max posts $max_posts = $options['max_posts']; if (!is_numeric($max_posts)) { $max_posts = 10; } // Build the filter $filters = array('showposts' => $max_posts, 'post_status' => 'publish'); if ($cat != '') { $filters['cat'] = $cat; } if (!empty($options['tags'])) { $filters['tag'] = str_replace(' ', '', $options['tags']); } if (!empty($options['post_types'])) { $post_types = $options['post_types']; if (!empty($post_types)) { $filters['post_type'] = $post_types; } } if (!empty($options['language'])) { if (class_exists('Polylang')) { $filters['lang'] = $options['language']; } if (class_exists('SitePress')) { $filters['suppress_filters'] = false; do_action('wpml_switch_language', $options['language']); } } $posts = get_posts($filters); if (empty($options['excerpt_length'])) { $options['excerpt_length'] = 30; } foreach ($posts as $post) { if (empty($post->post_excerpt)) { add_filter('strip_shortcodes_tagnames', array($this, 'hook_strip_shortcodes_tagnames'), 99, 1); $post->excerpt = wp_strip_all_tags(strip_shortcodes($post->post_content)); remove_filter('strip_shortcodes_tagnames', array($this, 'hook_strip_shortcodes_tagnames')); $post->excerpt = wp_trim_words($post->excerpt, $options['excerpt_length']); } else { $post->excerpt = wp_trim_words($post->post_excerpt, $options['excerpt_length']); } $post->title = $post->post_title; $post->content = do_shortcode($post->post_content); $post->content = wpautop($post->content); $post->link = get_permalink($post->ID); $post->images = array(); $post->images['thumbnail'] = NewsletterModule::get_post_image($post->ID, 'thumbnail'); $post->images['medium'] = NewsletterModule::get_post_image($post->ID, 'medium'); $post->images['large'] = NewsletterModule::get_post_image($post->ID, 'large'); } return $posts; } /** * Creates an email to be sent for real or for test. A set of options can be passed i place of the actual saved * module options. * * $last_run can be passed as timestamp to simulate a previous newsletter generation at thattime. * * @global Newsletter $newsletter * @global wpdb $wpdb * @return array Created email */ var $create_email_result; function create_email($channel, $last_run = null) { global $wpdb, $newsletter; $options = $channel->data; $id = $channel->id; $logger = $this->get_logger(); //$logger->debug('Creating email for channel ' . $channel->id); if ($last_run === null) { $last_run = $channel->last_run; } $email = ['options' => []]; if ($channel->theme_type == self::THEME_TYPE_COMPOSER) { $template = Newsletter::instance()->get_email($channel->email_id); $result = NewsletterEmails::instance()->regenerate($template, array('type' => 'automated', 'last_run' => $last_run)); //echo esc_html($template->message); //die(); if (!$result) { return false; } $email['message'] = $template->message; $email['message_text'] = "This email requires a modern e-mail reader but you can view the email online here:\n{email_url}.\nThank you, " . wp_specialchars_decode(get_option('blogname'), ENT_QUOTES) . "\nTo change your subscription follow: {profile_url}."; $email['subject'] = 'Latest news from {blog_title}'; if (!empty($options['subject'])) { $email['subject'] = $options['subject']; $email['subject'] = str_replace('{dynamic_subject}', $template->subject, $email['subject']); } else if (!empty($template->subject)) { $email['subject'] = $template->subject; } } else { $posts = array(); // Old themes rely on post extracted by Automated, new theme do themself if (empty($options['new_theme'])) { // Load posts using the filters specified in the channel configuration $posts = $this->get_posts($options, $id); if (!empty($posts)) { if ($last_run >= $this->m2t($posts[0]->post_date_gmt)) { $logger->debug('The latest extracted post is older than the last generation time. Post list will be emptied.'); $this->create_email_result = 'The most recent post is too old'; $posts = array(); } } // Here we have an array of posts, empty of there are no posts available after the last generation date // We can proceed if the configuration allow it if (empty($posts) && empty($options['ignore_no_new_posts'])) { $logger->debug('The post list is empty, exit'); // Rather odd, it means the blog has not published posts... $this->create_email_result = 'No posts found, has the blog published posts?'; return false; } if (empty($posts)) { $new_posts = array(); $old_posts = array(); } else { list($new_posts, $old_posts) = $this->split_posts($posts, $last_run); } } // Channels have theme options inside the channel options $theme = $this->get_theme($options['theme'], true); $theme_options = $options; $theme_defaults_file = $theme['dir'] . '/theme-defaults.php'; if (file_exists($theme_defaults_file)) { @include $theme_defaults_file; if (isset($theme_defaults) && is_array($theme_defaults)) { $theme_options = array_merge($theme_defaults, $theme_options); } } $main_options = Newsletter::instance()->options; foreach ($main_options as $key => $value) { $theme_options['main_' . $key] = $value; } $theme_url = $this->get_theme_url($options['theme']); $theme_subject = trim($options['subject']); // The subject can be changed by the theme ob_start(); require $this->get_theme_file($options['theme'], 'theme.php'); $email['message'] = ob_get_clean(); if (empty($email['message'])) { $logger->info('Theme returned an empty message'); $this->create_email_result = 'The theme returned an empty message'; return false; } $email['message'] = $this->inline_css($email['message']); if (!empty($theme_subject)) { $email['subject'] = $theme_subject; } else { if (!empty($posts)) { $email['subject'] = $posts[0]->post_title; } else { $email['subject'] = 'Subject not set'; } } if (!empty($posts)) { $email['subject'] = str_replace('{last_post_title}', $posts[0]->post_title, $email['subject']); } else { $email['subject'] = str_replace('{last_post_title}', '', $email['subject']); } $email['message_text'] = 'This message can be viewed only with a modern email client, sorry.'; $file = $this->get_theme_file($options['theme'], 'theme-text.php'); if (is_file($file)) { ob_start(); include $file; $email['message_text'] = ob_get_clean(); } // END CLASSIC THEME } // Truncates too long subjects (if there is not mb_strlen, use strlen even if it wrongly find the length of utf-8 strings) if (mb_strlen($email['subject']) > 250) { $x = strrpos($email['subject'], ' ', 250); $email['subject'] = substr($email['subject'], 0, $x); } $email['subject'] = $newsletter->replace_date($email['subject']); $email['message'] = $newsletter->replace_date($email['message']); $email['total'] = $this->get_subscriber_count($channel); $email['query'] = $this->generate_subscribers_list_query($channel); $email['type'] = 'automated_' . $id; $email['send_on'] = time(); $email['status'] = 'sending'; $email['track'] = $options['track']; $email['message'] = str_replace('{subject}', $email['subject'], $email['message']); $email['message'] = str_replace('{email_subject}', $email['subject'], $email['message']); $email['options'] = array(); if (isset($options['utm_campaign'])) { $email['options']['utm_campaign'] = $options['utm_campaign']; $email['options']['utm_source'] = $options['utm_source']; $email['options']['utm_medium'] = $options['utm_medium']; $email['options']['utm_term'] = $options['utm_term']; $email['options']['utm_content'] = $options['utm_content']; } // Sender name and email if (!empty($options['sender_name'])) { $email['options']['sender_name'] = $options['sender_name']; } if (!empty($options['sender_email'])) { $email['options']['sender_email'] = $options['sender_email']; } return $email; } static function get_last_email($id = null) { global $wpdb; $id = (int) $id; // Limit the fields returned to the minimum $email = $wpdb->get_row("select id, send_on, status, sent, total from " . NEWSLETTER_EMAILS_TABLE . " where type='automated_$id' order by id desc limit 1", OBJECT); if ($wpdb->last_error) { return false; } return $email; } static function get_emails($id = null, $max = false) { global $wpdb; $id = (int) $id; $query = "select * from " . NEWSLETTER_EMAILS_TABLE . " where type='automated_$id' order by id desc"; if ($max) $query .= ' limit ' . $max; $list = $wpdb->get_results($query, OBJECT); if ($wpdb->last_error) { return false; } if (empty($list)) { return array(); } return $list; } /** * Returns all the available themes. The list is a set of arrays with keys: * * dir - the path to the theme * name - the theme name * */ function get_themes() { static $list = array(); // Caching if (!empty($list)) return $list; $logger = $this->get_logger(); $dir = dirname(__FILE__) . '/themes'; $handle = @opendir($dir); if ($handle !== false) { while ($file = readdir($handle)) { if ($file == '.' || $file == '..') { continue; } if (!@is_file($dir . '/' . $file . '/theme.php')) { continue; } $list[$file] = array('dir' => $dir . '/' . $file, 'name' => $file, 'type' => 'old'); } closedir($handle); } $dir = WP_CONTENT_DIR . '/extensions/newsletter-automated/themes'; if (is_dir($dir)) { $handle = @opendir($dir); if ($handle !== false) { while ($file = readdir($handle)) { if ($file == '.' || $file == '..') { continue; } // Theme already registered with that name if (isset($list[$file])) { $logger->error('Theme ' . $file . ' in extensions folder already registered'); continue; } if (!@is_file($dir . '/' . $file . '/theme.php')) { continue; } $list[$file] = array('dir' => $dir . '/' . $file, 'name' => $file, 'type' => 'old'); } closedir($handle); } } $extra = array(); $extra = apply_filters('newsletter_automated_themes', $extra); // [TODO] On windows it may not work foreach ($extra as $dir) { $dir = wp_normalize_path($dir); if (!file_exists($dir . '/theme.php')) { continue; } if (isset($list[basename($dir)])) { $logger->error('Theme in ' . $dir . ' folder already registered'); continue; } $data = get_file_data($dir . '/theme.php', array('name' => 'Name')); // Should never happen if (empty($data['name'])) { $data['name'] = basename($dir); $data['type'] = 'old'; } $data['dir'] = $dir; $list[basename($dir)] = $data; } return $list; } function get_theme($id, $fallback = false) { $themes = $this->get_themes(); if (isset($themes[$id])) { return $themes[$id]; } if ($fallback) { return $themes['default']; } return null; } function get_theme_options($theme) { return get_option('newsletter_automated_theme_' . $theme, array()); } function get_theme_url($id) { $theme = $this->get_theme($id); $path = substr($theme['dir'], strlen(WP_CONTENT_DIR)); return content_url($path); } /** * Returns the full path to a theme file. * * @param string $id * @param string $file * @return string */ function get_theme_file($id, $file) { $theme = $this->get_theme($id); return $theme['dir'] . '/' . $file; } function set_last_run($id, $time) { global $wpdb; $this->query($wpdb->prepare("update {$wpdb->prefix}newsletter_automated set last_run=%d where id=%d limit 1", $time, $id)); } function add_to_last_run($id, $delta) { global $wpdb; $delta = (int) $delta; $this->query($wpdb->prepare("update {$wpdb->prefix}newsletter_automated set last_run=last_run+({$delta}) where id=%d limit 1", $id)); } static function split_posts(&$posts, $time = 0) { if ($time < 0) { if (count($posts) > 1) { return array_chunk($posts, ceil(count($posts) / 2)); } else if (count($posts) == 0) { return array(array(), array()); } else { return array($posts, array()); } } $result = array(array(), array()); foreach ($posts as &$post) { if (self::is_post_old($post, $time)) $result[1][] = $post; else $result[0][] = $post; } return $result; } static function is_post_old(&$post, $time = 0) { return self::m2t($post->post_date_gmt) <= $time; } static function m2t($s) { // TODO: use the wordpress function I don't remeber the name $s = explode(' ', $s); $d = explode('-', $s[0]); $t = explode(':', $s[1]); return gmmktime((int) $t[0], (int) $t[1], (int) $t[2], (int) $d[1], (int) $d[2], (int) $d[0]); } function get_channels() { global $wpdb; static $channels = null; if (is_array($channels)) return $channels; $channels = $wpdb->get_results("select * from " . $wpdb->prefix . "newsletter_automated order by id"); foreach ($channels as $channel) { $channel->data = json_decode($channel->data, true); } return $channels; } function get_channel($id) { global $wpdb; $channel = $wpdb->get_row($wpdb->prepare("select * from " . $wpdb->prefix . "newsletter_automated where id=%d limit 1", $id)); if (!$channel) { return null; } $channel->data = json_decode($channel->data, true); if (!isset($channel->data['list'])) { $channel->data['list'] = ''; } if ($channel->theme_type == self::THEME_TYPE_COMPOSER && !$channel->email_id) { $email = []; $email['type'] = 'automated_template'; $email['editor'] = NewsletterEmails::EDITOR_COMPOSER; $email['message'] = $channel->theme; $email = NewsletterEmails::instance()->save_email($email); $wpdb->update($wpdb->prefix . "newsletter_automated", array('email_id' => $email->id), array('id' => $channel->id)); $channel->email_id = $email->id; } return $channel; } function inline_css($content, $strip_style_blocks = false) { return NewsletterEmails::instance()->inline_css($content, $strip_style_blocks); } /** * @param array $filter * @param boolean $get_count * * @return string */ function generate_subscribers_list_query($channel, $get_count = false) { $default_filter = [ 'list' => null, 'languages' => array(), 'status' => TNP_User::STATUS_CONFIRMED, ]; $filter = array( 'list' => (int) $channel->data['list'], 'languages' => isset($channel->data['languages']) ? $channel->data['languages'] : array() ); $filter = array_merge($default_filter, $filter); $status = $filter['status']; if (!$get_count) { $query = "SELECT * FROM "; } else { $query = "SELECT COUNT(*) FROM "; } $query .= NEWSLETTER_USERS_TABLE . " WHERE status='$status'"; if (!empty($filter['list'])) { $list = $filter['list']; $query .= " AND list_$list=1"; } if (!empty($filter['languages'])) { $query .= $this->generate_languages_in_clause($filter['languages']); } return $query; } /** * @param array $filter * * @return string */ function generate_subscribers_list_count_query($channel) { return $this->generate_subscribers_list_query($channel, true); } /** * @param array $languages_array * * @return string */ function generate_languages_in_clause($languages_array = array()) { if (!empty($languages_array)) { $languages = array_map(function ($lang) { $lang = esc_sql($lang); return "'$lang'"; }, $languages_array); $in_clause = '(' . implode(',', $languages) . ')'; return " and language IN $in_clause"; } return ''; } /** * Returns the number of subscribers targeted by the provided channel. * @global wpdb $wpdb * @param stdClass $channel * @return int */ function get_subscriber_count($channel) { global $wpdb; $query = $this->generate_subscribers_list_count_query($channel); return $wpdb->get_var($query); } }