PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.12.2
Independent Analytics – WordPress Analytics Plugin v2.12.2
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 11 months ago Interval.php 1 year ago Interval_Factory.php 2 years ago
Email_Reports.php
234 lines
1 <?php
2
3 namespace IAWP\Email_Reports;
4
5 use DateTime;
6 use IAWP\Email_Reports\Intervals\Monthly;
7 use IAWP\Rows\Campaigns;
8 use IAWP\Rows\Countries;
9 use IAWP\Rows\Device_Types;
10 use IAWP\Rows\Forms;
11 use IAWP\Rows\Link_Patterns;
12 use IAWP\Rows\Pages;
13 use IAWP\Rows\Referrers;
14 use IAWP\Sort_Configuration;
15 use IAWP\Statistics\Page_Statistics;
16 use IAWP\Statistics\Statistic;
17 use IAWP\Utils\Timezone;
18 use IAWP\Utils\URL;
19 /** @internal */
20 class Email_Reports
21 {
22 public function __construct()
23 {
24 $monitored_options = ['iawp_email_report_interval', 'iawp_email_report_time', 'iawp_email_report_email_addresses'];
25 foreach ($monitored_options as $option) {
26 \add_action('update_option_' . $option, [$this, 'schedule'], 10, 0);
27 \add_action('add_option_' . $option, [$this, 'schedule'], 10, 0);
28 }
29 // Maybe reschedule when starting day of the week is changed
30 \add_action('update_option_iawp_dow', [$this, 'maybe_reschedule'], 10, 0);
31 \add_action('add_option_iawp_dow', [$this, 'maybe_reschedule'], 10, 0);
32 \add_action('iawp_send_email_report', [$this, 'send_email_report']);
33 }
34 public function schedule()
35 {
36 $this->unschedule();
37 if (empty(\IAWPSCOPED\iawp()->get_option('iawp_email_report_email_addresses', []))) {
38 return;
39 }
40 \wp_schedule_event($this->interval()->next_interval_start()->getTimestamp(), $this->interval()->id(), 'iawp_send_email_report');
41 }
42 public function unschedule()
43 {
44 $timestamp = \wp_next_scheduled('iawp_send_email_report');
45 if (\is_int($timestamp)) {
46 \wp_unschedule_event($timestamp, 'iawp_send_email_report');
47 }
48 }
49 /**
50 * For testing purposes, get the
51 *
52 * @return DateTime
53 */
54 public function next_event_scheduled_at() : ?DateTime
55 {
56 if (!\wp_next_scheduled('iawp_send_email_report')) {
57 return null;
58 }
59 $date = new DateTime();
60 $date->setTimezone(Timezone::site_timezone());
61 $date->setTimestamp(\wp_next_scheduled('iawp_send_email_report'));
62 return $date;
63 }
64 public function next_email_at_for_humans() : string
65 {
66 if (!\wp_next_scheduled('iawp_send_email_report')) {
67 return \esc_html__('There is no email scheduled.', 'independent-analytics');
68 }
69 $date = $this->interval()->next_interval_start();
70 $day = $date->format(\get_option('date_format'));
71 $time = $date->format(\IAWPSCOPED\iawp()->get_option('time_format', 'g:i a'));
72 return \sprintf(\__('Next email scheduled for %s at %s.', 'independent-analytics'), '<span>' . $day . '</span>', '<span>' . $time . '</span>');
73 }
74 public function maybe_reschedule()
75 {
76 if (!\wp_next_scheduled('iawp_send_email_report')) {
77 return;
78 }
79 if (\IAWPSCOPED\iawp()->get_option('iawp_email_report_interval', 'monthly') != 'weekly') {
80 return;
81 }
82 $this->schedule();
83 }
84 public function send_email_report(bool $is_test_email = \false)
85 {
86 // Email reports should be scheduled every time. CRON jobs run on a fixed interval which cannot
87 // account for daylight saving times changes, causing them to send at the wrong hour as daylight
88 // savings time changes. Months also have a varying number of days and need to be rescheduled
89 // to consistently send on the correct day.
90 $this->schedule();
91 $to = \IAWPSCOPED\iawp()->get_option('iawp_email_report_email_addresses', []);
92 if (empty($to)) {
93 return;
94 }
95 $from = \IAWPSCOPED\iawp()->get_option('iawp_email_report_from_address', \get_option('admin_email'));
96 $reply_to = \IAWPSCOPED\iawp()->get_option('iawp_email_report_reply_to_address', \get_option('admin_email'));
97 $body = $this->get_email_body();
98 if (\count($to) > 1) {
99 for ($i = 0; $i < \count($to); $i++) {
100 if ($i == 0) {
101 continue;
102 }
103 $headers[] = 'Bcc: ' . $to[$i];
104 }
105 }
106 $headers[] = 'From: ' . \get_bloginfo('name') . ' <' . \esc_attr($from) . '>';
107 $headers[] = 'Reply-To: ' . \esc_attr($reply_to);
108 $headers[] = 'Content-Type: text/html; charset=UTF-8';
109 // Prevents WP HTML Mail plugin from breaking email design (https://wordpress.org/plugins/wp-html-mail/)
110 \add_filter('haet_mail_use_template', function () {
111 return \false;
112 });
113 return \wp_mail($to[0], $this->subject_line($is_test_email), $body, $headers);
114 }
115 public function get_email_body($colors = '')
116 {
117 $statistics = new Page_Statistics($this->interval()->date_range());
118 $quick_stats = \array_values(\array_filter($statistics->get_statistics(), function (Statistic $statistics) {
119 return $statistics->is_visible() && $statistics->is_group_plugin_enabled();
120 }));
121 $chart = new \IAWP\Email_Reports\Email_Chart($statistics);
122 $colors = $colors == '' ? \IAWPSCOPED\iawp()->get_option('iawp_email_report_colors', ['#5123a0', '#fafafa', '#3a1e6b', '#fafafa', '#5123a0', '#a985e6', '#ece9f2', '#f7f5fa', '#ece9f2', '#dedae6']) : \explode(',', $colors);
123 $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())));
124 return \IAWPSCOPED\iawp_blade()->run('email.email', [
125 'site_title' => \get_bloginfo('name'),
126 'site_url' => URL::new(\get_site_url())->get_domain(),
127 'date' => $this->interval()->report_time_period_for_humans(),
128 // The value that needs to change
129 'stats' => $quick_stats,
130 'top_ten' => $this->get_top_ten(),
131 'chart_views' => $chart->views,
132 'chart_title' => $this->interval()->chart_title(),
133 'most_views' => $chart->most_views,
134 'y_labels' => $chart->y_labels,
135 'x_labels' => $chart->x_labels,
136 'colors' => $colors,
137 'footer_text' => $footer_text,
138 ]);
139 }
140 private function interval() : \IAWP\Email_Reports\Interval
141 {
142 return \IAWP\Email_Reports\Interval_Factory::from_option();
143 }
144 private function subject_line(bool $is_test_email) : string
145 {
146 $parts = [];
147 if ($is_test_email) {
148 $parts[] = \__('[Test]', 'independent-analytics');
149 }
150 $parts[] = \__('Analytics Report for', 'independent-analytics');
151 $parts[] = \get_bloginfo('name');
152 $parts[] = '[' . $this->interval()->report_time_period_for_humans() . ']';
153 return \esc_html(\implode(' ', $parts));
154 }
155 private function get_top_ten() : array
156 {
157 $date_range = $this->interval()->date_range();
158 $queries = ['pages' => 'title', 'referrers' => 'referrer', 'countries' => 'country', 'devices' => 'device_type', 'campaigns' => 'title', 'forms' => 'form_title', 'clicks' => 'link_name', 'landing_pages' => 'title', 'exit_pages' => 'title'];
159 $top_ten = [];
160 $sort_configuration = new Sort_Configuration('views', 'desc');
161 $title = '';
162 foreach ($queries as $type => $title) {
163 if ($type === 'pages') {
164 $query = new Pages($date_range, 10, null, $sort_configuration);
165 $title = \esc_html__('Pages', 'independent-analytics');
166 } elseif ($type === 'referrers') {
167 $query = new Referrers($date_range, 10, null, $sort_configuration);
168 $title = \esc_html__('Referrers', 'independent-analytics');
169 } elseif ($type === 'countries') {
170 $query = new Countries($date_range, 10, null, $sort_configuration);
171 $title = \esc_html__('Countries', 'independent-analytics');
172 } elseif ($type === 'devices') {
173 $query = new Device_Types($date_range, 10, null, $sort_configuration);
174 $title = \esc_html__('Devices', 'independent-analytics');
175 } elseif ($type === 'campaigns') {
176 $query = new Campaigns($date_range, 10, null, $sort_configuration);
177 $title = \esc_html__('Campaigns', 'independent-analytics');
178 } elseif ($type === 'forms') {
179 $query = new Forms($date_range, 10, null, new Sort_Configuration('submissions', 'desc'));
180 $title = \esc_html__('Forms', 'independent-analytics');
181 } elseif ($type === 'clicks') {
182 $query = new Link_Patterns($date_range, 10, null, new Sort_Configuration('link_clicks', 'desc'));
183 $title = \esc_html__('Link Patterns', 'independent-analytics');
184 } elseif ($type === 'landing_pages') {
185 $query = new Pages($date_range, 10, null, new Sort_Configuration('entrances', 'desc'));
186 $title = \esc_html__('Landing Pages', 'independent-analytics');
187 } elseif ($type === 'exit_pages') {
188 $query = new Pages($date_range, 10, null, new Sort_Configuration('exits', 'desc'));
189 $title = \esc_html__('Exit Pages', 'independent-analytics');
190 } else {
191 continue;
192 }
193 $rows = \array_map(function ($row, $index) use($type) {
194 if ($type == 'referrers') {
195 $edited_title = $row->referrer();
196 } elseif ($type == 'countries') {
197 $edited_title = $row->country();
198 } elseif ($type == 'devices') {
199 $edited_title = $row->device_type();
200 } elseif ($type == 'campaigns') {
201 $edited_title = $row->utm_campaign();
202 } elseif ($type == 'forms') {
203 $edited_title = $row->form_title();
204 } elseif ($type == 'clicks') {
205 $edited_title = $row->link_name();
206 } else {
207 $edited_title = $row->title();
208 }
209 $edited_title = \mb_strlen($edited_title) > 30 ? \mb_substr($edited_title, 0, 30) . '...' : $edited_title;
210 $metric = 'views';
211 if ($type == 'clicks') {
212 $metric = 'link_clicks';
213 } elseif ($type == 'forms') {
214 $metric = 'submissions';
215 } elseif ($type == 'landing_pages') {
216 $metric = 'entrances';
217 } elseif ($type == 'exit_pages') {
218 $metric = 'exits';
219 }
220 return ['title' => $edited_title, 'views' => $row->{$metric}()];
221 }, $query->rows(), \array_keys($query->rows()));
222 if (\count($rows) == 0) {
223 continue;
224 }
225 // Remove landing page and exit rows with 0 entrances/exits
226 $rows = \array_filter($rows, function ($row) {
227 return $row['views'] > 0;
228 });
229 $top_ten[$type] = ['title' => $title, 'rows' => $rows];
230 }
231 return $top_ten;
232 }
233 }
234