PluginProbe ʕ •ᴥ•ʔ
Custom Twitter Feeds – A Tweets Widget or X Feed Widget / 2.7.0
Custom Twitter Feeds – A Tweets Widget or X Feed Widget v2.7.0
2.8.0 2.7.0 2.6.1 2.6.0 1.0 1.0.1 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.2 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.4 1.4.1 1.5 1.5.1 1.6 1.6.1 1.7 1.8 1.8.1 1.8.2 1.8.3 1.8.4 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1 2.1.1 2.1.2 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.5.4 2.5.5 trunk
custom-twitter-feeds / inc / UsageTracking / SmashUsageTracking.php
custom-twitter-feeds / inc / UsageTracking Last commit date
Core 1 month ago Twitter 1 month ago Config.php 1 month ago EventRecorder.php 1 month ago ReporterInterface.php 1 month ago SmashUsageTracking.php 1 month ago
SmashUsageTracking.php
235 lines
1 <?php
2 /**
3 * Orchestrates Smash Usage Tracking: schedule, ensure site token, build payload, send.
4 *
5 * @package TwitterFeed\UsageTracking
6 * @since 2.6
7 */
8
9 namespace TwitterFeed\UsageTracking;
10
11 use TwitterFeed\UsageTracking\Core\RegisterSite;
12 use TwitterFeed\UsageTracking\Core\Sender;
13 use TwitterFeed\UsageTracking\Core\PayloadBuilder;
14 use TwitterFeed\UsageTracking\Core\Scheduler;
15 use TwitterFeed\UsageTracking\Twitter\TwitterFreeReporter;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 class SmashUsageTracking {
22
23 /** @var ReporterInterface */
24 private $reporter;
25
26 /** @var RegisterSite */
27 private $register_site;
28
29 /** @var Sender */
30 private $sender;
31
32 /** @var PayloadBuilder */
33 private $payload_builder;
34
35 /** @var Scheduler */
36 private $scheduler;
37
38 public function __construct() {
39 $this->reporter = new TwitterFreeReporter();
40 $this->register_site = new Core\RegisterSite();
41 $this->sender = new Core\Sender();
42 $this->payload_builder = new Core\PayloadBuilder( $this->reporter );
43 $this->scheduler = new Core\Scheduler();
44
45 add_action( 'init', array( $this, 'maybe_schedule' ) );
46 add_filter( 'cron_schedules', array( $this->scheduler, 'add_schedules' ) );
47 add_action( Config::CRON_HOOK, array( $this, 'send_checkin' ) );
48
49 add_action( 'current_screen', array( $this, 'maybe_record_active_day' ) );
50 add_action( 'wp_ajax_ctf_smash_usage_record_event', array( $this, 'ajax_record_event' ) );
51 add_action( 'wp_ajax_ctf_smash_usage_record_session', array( $this, 'ajax_record_session' ) );
52 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_session_script' ), 20 );
53 }
54
55 /**
56 * Schedule cron if enabled and not already scheduled.
57 */
58 public function maybe_schedule() {
59 $this->scheduler->schedule();
60 }
61
62 /**
63 * Cron callback: ensure site token, build payload, send, update last_send.
64 */
65 public function send_checkin() {
66 if ( ! Config::is_enabled() ) {
67 return;
68 }
69
70 $host = wp_parse_url( home_url(), PHP_URL_HOST );
71 if ( 'smashballoon.com' === $host || '.smashballoon.com' === substr( (string) $host, -17 ) ) {
72 return;
73 }
74
75 $opt = get_option( Config::OPTION_TRACKING, array() );
76 $last_send = is_array( $opt ) && isset( $opt['last_send'] ) ? (int) $opt['last_send'] : 0;
77 if ( $last_send > strtotime( '-1 week' ) ) {
78 return;
79 }
80
81 $site_token = get_option( Config::OPTION_SITE_TOKEN, '' );
82 if ( '' === $site_token || ! is_string( $site_token ) ) {
83 $site_token = $this->register_site->register( $this->reporter );
84 if ( null === $site_token ) {
85 return;
86 }
87 }
88
89 $period_end = gmdate( 'Y-m-d', strtotime( 'yesterday' ) );
90 $period_start = gmdate( 'Y-m-d', strtotime( $period_end . ' -6 days' ) );
91
92 $payload = $this->payload_builder->build( $site_token, $period_start, $period_end );
93 $success = $this->sender->send( $payload );
94
95 if ( $success ) {
96 update_option(
97 Config::OPTION_TRACKING,
98 array(
99 'enabled' => Config::is_enabled(),
100 'last_send' => time(),
101 ),
102 false
103 );
104 $sent_events = isset( $payload['dynamic_metrics']['events'] ) && is_array( $payload['dynamic_metrics']['events'] )
105 ? $payload['dynamic_metrics']['events']
106 : array();
107 $this->reset_events_after_send( $sent_events );
108 }
109 }
110
111 /**
112 * Remove only the event keys that were included in the sent payload.
113 * Events recorded by concurrent AJAX requests after the payload was built
114 * are left intact so they roll over into the next reporting period.
115 *
116 * @param array $sent_events Events map from the sent payload.
117 */
118 private function reset_events_after_send( array $sent_events ) {
119 if ( ! empty( $sent_events ) ) {
120 $stored = get_option( EventRecorder::OPTION_NAME, array() );
121 if ( is_array( $stored ) ) {
122 foreach ( array_keys( $sent_events ) as $key ) {
123 unset( $stored[ $key ] );
124 }
125 update_option( EventRecorder::OPTION_NAME, $stored, false );
126 }
127 }
128 update_option( Config::OPTION_SESSION_DURATIONS, array(), false );
129 }
130
131 /**
132 * Unschedule cron (call when disabling tracking).
133 */
134 public function unschedule() {
135 $this->scheduler->unschedule();
136 }
137
138 /**
139 * Record active day and optionally settings_page_viewed when on a CTF admin page.
140 */
141 public function maybe_record_active_day( $screen ) {
142 if ( ! $screen || strpos( $screen->id, 'twitter-feed' ) === false ) {
143 return;
144 }
145 if ( ! Config::is_enabled() ) {
146 return;
147 }
148 EventRecorder::record_active_day();
149 if ( isset( $screen->id ) && strpos( $screen->id, 'ctf-settings' ) !== false ) {
150 EventRecorder::record( 'settings_page_viewed' );
151 }
152 }
153
154 /**
155 * AJAX: record a single event from JS (e.g. setup_wizard_started).
156 */
157 public function ajax_record_event() {
158 check_ajax_referer( 'ctf_smash_usage_record_event', 'nonce' );
159 if ( ! current_user_can( 'manage_twitter_feed_options' ) && ! current_user_can( 'manage_options' ) ) {
160 wp_send_json_error();
161 }
162 $event_name = isset( $_POST['event_name'] ) ? sanitize_text_field( wp_unslash( $_POST['event_name'] ) ) : '';
163 $allowed = array(
164 'setup_wizard_started',
165 'setup_wizard_step_completed',
166 'setup_wizard_completed',
167 'setup_wizard_abandoned',
168 'upgrade_prompt_shown',
169 );
170 if ( '' !== $event_name && in_array( $event_name, $allowed, true ) ) {
171 EventRecorder::record( $event_name );
172 }
173 wp_send_json_success();
174 }
175
176 /**
177 * AJAX: record session duration from JS (seconds).
178 */
179 public function ajax_record_session() {
180 check_ajax_referer( 'ctf_smash_usage_record_session', 'nonce' );
181 if ( ( ! current_user_can( 'manage_twitter_feed_options' ) && ! current_user_can( 'manage_options' )) || ! Config::is_enabled() ) {
182 wp_send_json_error();
183 }
184 $seconds = isset( $_POST['duration_seconds'] ) ? (int) $_POST['duration_seconds'] : 0;
185 EventRecorder::record_session_duration( $seconds );
186 wp_send_json_success();
187 }
188
189 /**
190 * Enqueue session-duration and record-event script on CTF admin pages.
191 */
192 public function enqueue_session_script() {
193 if ( ! function_exists( 'get_current_screen' ) ) {
194 return;
195 }
196 $screen = get_current_screen();
197 if ( ! $screen || strpos( $screen->id, 'twitter-feed' ) === false ) {
198 return;
199 }
200 $script_path = 'admin/assets/js/smash-usage-session.js';
201 $path = dirname( __DIR__, 2 ) . '/' . $script_path;
202 if ( ! file_exists( $path ) ) {
203 return;
204 }
205 wp_enqueue_script(
206 'ctf-smash-usage-session',
207 CTF_PLUGIN_URL . $script_path,
208 array( 'jquery' ),
209 defined( 'CTF_VERSION' ) ? CTF_VERSION : '1.0',
210 true
211 );
212 wp_localize_script(
213 'ctf-smash-usage-session',
214 'ctfSmashUsageSession',
215 array(
216 'ajax_url' => admin_url( 'admin-ajax.php' ),
217 'nonce' => wp_create_nonce( 'ctf_smash_usage_record_session' ),
218 'event_nonce' => wp_create_nonce( 'ctf_smash_usage_record_event' ),
219 )
220 );
221 }
222
223 /**
224 * Build the usage report payload without sending (for preview/debugging).
225 *
226 * @return array
227 */
228 public function get_payload_preview() {
229 $period_end = gmdate( 'Y-m-d', strtotime( 'yesterday' ) );
230 $period_start = gmdate( 'Y-m-d', strtotime( $period_end . ' -6 days' ) );
231
232 return $this->payload_builder->build( 'preview-no-api', $period_start, $period_end );
233 }
234 }
235