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