PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.14.0
Independent Analytics – WordPress Analytics Plugin v2.14.0
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / IAWP / Email_Reports / Email_Reports.php
independent-analytics / IAWP / Email_Reports Last commit date
Email_Chart.php 1 year ago Email_Reports.php 7 months ago Interval.php 1 year ago Interval_Factory.php 2 years ago
Email_Reports.php
249 lines
1 <?php
2
3 namespace IAWP\Email_Reports;
4
5 use DateTime;
6 use IAWP\Rows\Campaigns;
7 use IAWP\Rows\Countries;
8 use IAWP\Rows\Device_Types;
9 use IAWP\Rows\Forms;
10 use IAWP\Rows\Link_Patterns;
11 use IAWP\Rows\Pages;
12 use IAWP\Rows\Referrers;
13 use IAWP\Sort_Configuration;
14 use IAWP\Statistics\Page_Statistics;
15 use IAWP\Statistics\Statistic;
16 use IAWP\Tables\Columns\Column;
17 use IAWP\Tables\Table_Campaigns;
18 use IAWP\Tables\Table_Clicks;
19 use IAWP\Tables\Table_Devices;
20 use IAWP\Tables\Table_Geo;
21 use IAWP\Tables\Table_Pages;
22 use IAWP\Tables\Table_Referrers;
23 use IAWP\Utils\Timezone;
24 use IAWP\Utils\URL;
25 /** @internal */
26 class Email_Reports
27 {
28 public function __construct()
29 {
30 $monitored_options = ['iawp_email_report_interval', 'iawp_email_report_time', 'iawp_email_report_email_addresses', 'iawp_email_report_paused'];
31 foreach ($monitored_options as $option) {
32 \add_action('update_option_' . $option, [$this, 'schedule'], 10, 0);
33 \add_action('add_option_' . $option, [$this, 'schedule'], 10, 0);
34 }
35 // Maybe reschedule when starting day of the week is changed
36 \add_action('update_option_iawp_dow', [$this, 'maybe_reschedule'], 10, 0);
37 \add_action('add_option_iawp_dow', [$this, 'maybe_reschedule'], 10, 0);
38 \add_action('iawp_send_email_report', [$this, 'send_email_report']);
39 }
40 public function schedule()
41 {
42 \wp_unschedule_hook('iawp_send_email_report');
43 if (\get_option('iawp_email_report_paused', '0') === '1') {
44 return;
45 }
46 if (empty(\IAWPSCOPED\iawp()->get_option('iawp_email_report_email_addresses', []))) {
47 return;
48 }
49 \wp_schedule_event($this->interval()->next_interval_start()->getTimestamp(), $this->interval()->id(), 'iawp_send_email_report');
50 }
51 /**
52 * For testing purposes, get the
53 *
54 * @return DateTime
55 */
56 public function next_event_scheduled_at() : ?DateTime
57 {
58 if (!\wp_next_scheduled('iawp_send_email_report')) {
59 return null;
60 }
61 $date = new DateTime();
62 $date->setTimezone(Timezone::site_timezone());
63 $date->setTimestamp(\wp_next_scheduled('iawp_send_email_report'));
64 return $date;
65 }
66 public function next_email_at_for_humans() : string
67 {
68 if (!\wp_next_scheduled('iawp_send_email_report')) {
69 return \esc_html__('There is no email scheduled.', 'independent-analytics');
70 }
71 $date = $this->interval()->next_interval_start();
72 $day = $date->format(\get_option('date_format'));
73 $time = $date->format(\IAWPSCOPED\iawp()->get_option('time_format', 'g:i a'));
74 return \sprintf(\__('Next email scheduled for %s at %s.', 'independent-analytics'), '<span>' . $day . '</span>', '<span>' . $time . '</span>');
75 }
76 public function maybe_reschedule()
77 {
78 if (!\wp_next_scheduled('iawp_send_email_report')) {
79 return;
80 }
81 if (\IAWPSCOPED\iawp()->get_option('iawp_email_report_interval', 'monthly') != 'weekly') {
82 return;
83 }
84 $this->schedule();
85 }
86 public function send_email_report(bool $is_test_email = \false)
87 {
88 // Email reports should be scheduled every time. CRON jobs run on a fixed interval which cannot
89 // account for daylight saving times changes, causing them to send at the wrong hour as daylight
90 // savings time changes. Months also have a varying number of days and need to be rescheduled
91 // to consistently send on the correct day.
92 $this->schedule();
93 $to = \IAWPSCOPED\iawp()->get_option('iawp_email_report_email_addresses', []);
94 if (empty($to)) {
95 return;
96 }
97 if (\get_option('iawp_email_report_paused', '0') === '1') {
98 return;
99 }
100 $from = \IAWPSCOPED\iawp()->get_option('iawp_email_report_from_address', \get_option('admin_email'));
101 $reply_to = \IAWPSCOPED\iawp()->get_option('iawp_email_report_reply_to_address', \get_option('admin_email'));
102 $body = $this->get_email_body();
103 if (\count($to) > 1) {
104 for ($i = 0; $i < \count($to); $i++) {
105 if ($i == 0) {
106 continue;
107 }
108 $headers[] = 'Bcc: ' . $to[$i];
109 }
110 }
111 $headers[] = 'From: ' . \get_bloginfo('name') . ' <' . \esc_attr($from) . '>';
112 $headers[] = 'Reply-To: ' . \esc_attr($reply_to);
113 $headers[] = 'Content-Type: text/html; charset=UTF-8';
114 // Prevents WP HTML Mail plugin from breaking email design (https://wordpress.org/plugins/wp-html-mail/)
115 \add_filter('haet_mail_use_template', function () {
116 return \false;
117 });
118 return \wp_mail($to[0], $this->subject_line($is_test_email), $body, $headers);
119 }
120 public function get_email_body($colors = '')
121 {
122 $statistics = new Page_Statistics($this->interval()->date_range());
123 $quick_stats = \array_values(\array_filter($statistics->get_statistics(), function (Statistic $statistics) {
124 return $statistics->is_visible() && $statistics->is_group_plugin_enabled();
125 }));
126 $chart = new \IAWP\Email_Reports\Email_Chart($statistics);
127 $colors = $colors == '' ? \IAWPSCOPED\iawp()->get_option('iawp_email_report_colors', ['#5123a0', '#fafafa', '#3a1e6b', '#fafafa', '#5123a0', '#a985e6', '#ece9f2', '#f7f5fa', '#ece9f2', '#dedae6']) : \explode(',', $colors);
128 $footer_text = \IAWPSCOPED\iawp()->get_option('iawp_email_report_footer', \sprintf(\esc_html__('This email was generated and delivered by %s', 'independent-analytics'), \esc_url(\get_site_url())));
129 return \IAWPSCOPED\iawp_blade()->run('email.email', [
130 'site_title' => \get_bloginfo('name'),
131 'site_url' => URL::new(\get_site_url())->get_domain(),
132 'date' => $this->interval()->report_time_period_for_humans(),
133 // The value that needs to change
134 'stats' => $quick_stats,
135 'top_ten' => $this->get_top_ten(),
136 'chart_views' => $chart->views,
137 'chart_title' => $this->interval()->chart_title(),
138 'most_views' => $chart->most_views,
139 'y_labels' => $chart->y_labels,
140 'x_labels' => $chart->x_labels,
141 'colors' => $colors,
142 'footer_text' => $footer_text,
143 ]);
144 }
145 private function interval() : \IAWP\Email_Reports\Interval
146 {
147 return \IAWP\Email_Reports\Interval_Factory::from_option();
148 }
149 private function subject_line(bool $is_test_email) : string
150 {
151 $parts = [];
152 if ($is_test_email) {
153 $parts[] = \__('[Test]', 'independent-analytics');
154 }
155 $parts[] = \__('Analytics Report for', 'independent-analytics');
156 $parts[] = \get_bloginfo('name');
157 $parts[] = '[' . $this->interval()->report_time_period_for_humans() . ']';
158 return \esc_html(\implode(' ', $parts));
159 }
160 private function get_top_ten() : array
161 {
162 $date_range = $this->interval()->date_range();
163 $queries = ['pages' => 'title', 'referrers' => 'referrer', 'countries' => 'country', 'devices' => 'device_type', 'campaigns' => 'title', 'forms' => 'form_title', 'clicks' => 'link_name', 'landing_pages' => 'title', 'exit_pages' => 'title'];
164 $top_ten = [];
165 $title = '';
166 foreach ($queries as $type => $title) {
167 if ($type === 'pages') {
168 $pages_table = new Table_Pages();
169 $query = new Pages($date_range, $pages_table->sanitize_sort_parameters(), 10);
170 $title = \esc_html__('Pages', 'independent-analytics');
171 } elseif ($type === 'referrers') {
172 $referrers_table = new Table_Referrers();
173 $query = new Referrers($date_range, $referrers_table->sanitize_sort_parameters(), 10);
174 $title = \esc_html__('Referrers', 'independent-analytics');
175 } elseif ($type === 'countries') {
176 $geo_table = new Table_Geo();
177 $query = new Countries($date_range, $geo_table->sanitize_sort_parameters(), 10);
178 $title = \esc_html__('Countries', 'independent-analytics');
179 } elseif ($type === 'devices') {
180 $devices_table = new Table_Devices();
181 $query = new Device_Types($date_range, $devices_table->sanitize_sort_parameters(), 10);
182 $title = \esc_html__('Devices', 'independent-analytics');
183 } elseif ($type === 'campaigns') {
184 $campaigns_table = new Table_Campaigns();
185 $query = new Campaigns($date_range, $campaigns_table->sanitize_sort_parameters(), 10);
186 $title = \esc_html__('Campaigns', 'independent-analytics');
187 } elseif ($type === 'forms') {
188 // This is a special case for form submissions which doesn't have an associated table
189 $column = new Column(['id' => 'submissions', 'name' => \__('Submissions', 'independent-analytics'), 'type' => 'int']);
190 $sort_configuration = new Sort_Configuration($column);
191 $query = new Forms($date_range, $sort_configuration, 10);
192 $title = \esc_html__('Forms', 'independent-analytics');
193 } elseif ($type === 'clicks') {
194 $clicks_table = new Table_Clicks();
195 $query = new Link_Patterns($date_range, $clicks_table->sanitize_sort_parameters(), 10);
196 $title = \esc_html__('Link Patterns', 'independent-analytics');
197 } elseif ($type === 'landing_pages') {
198 $pages_table = new Table_Pages();
199 $query = new Pages($date_range, $pages_table->sanitize_sort_parameters('entrances'), 10);
200 $title = \esc_html__('Landing Pages', 'independent-analytics');
201 } elseif ($type === 'exit_pages') {
202 $pages_table = new Table_Pages();
203 $query = new Pages($date_range, $pages_table->sanitize_sort_parameters('exits'), 10);
204 $title = \esc_html__('Exit Pages', 'independent-analytics');
205 } else {
206 continue;
207 }
208 $rows = \array_map(function ($row, $index) use($type) {
209 if ($type == 'referrers') {
210 $edited_title = $row->referrer();
211 } elseif ($type == 'countries') {
212 $edited_title = $row->country();
213 } elseif ($type == 'devices') {
214 $edited_title = $row->device_type();
215 } elseif ($type == 'campaigns') {
216 $edited_title = $row->utm_campaign();
217 } elseif ($type == 'forms') {
218 $edited_title = $row->form_title();
219 } elseif ($type == 'clicks') {
220 $edited_title = $row->link_name();
221 } else {
222 $edited_title = $row->title();
223 }
224 $edited_title = \mb_strlen($edited_title) > 30 ? \mb_substr($edited_title, 0, 30) . '...' : $edited_title;
225 $metric = 'views';
226 if ($type == 'clicks') {
227 $metric = 'link_clicks';
228 } elseif ($type == 'forms') {
229 $metric = 'submissions';
230 } elseif ($type == 'landing_pages') {
231 $metric = 'entrances';
232 } elseif ($type == 'exit_pages') {
233 $metric = 'exits';
234 }
235 return ['title' => $edited_title, 'views' => $row->{$metric}()];
236 }, $query->rows(), \array_keys($query->rows()));
237 if (\count($rows) == 0) {
238 continue;
239 }
240 // Remove landing page and exit rows with 0 entrances/exits
241 $rows = \array_filter($rows, function ($row) {
242 return $row['views'] > 0;
243 });
244 $top_ten[$type] = ['title' => $title, 'rows' => $rows];
245 }
246 return $top_ten;
247 }
248 }
249