PluginProbe
WebberZone Top 10 — Popular Posts / 4.5.1
WebberZone Top 10 — Popular Posts v4.5.1
4.5.1 4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 All 117 releases
← All changes | includes/admin/class-statistics-table.php +352 -80 4.4.34.5.1 View file →
@@ -57,8 +57,237 @@
57 57 $this->network_wide = $network_wide;
58 58 }
59 59
60 60 /**
61 + * Get the fixed IDs reserved for site-wide tracking contexts.
62 + *
63 + * The class only exists in the Pro build. Keeping this lookup optional lets
64 + * the shared statistics table continue to work in the free plugin.
65 + *
66 + * @return array<int,int> Reserved context IDs.
67 + */
68 + protected function get_sitewide_context_ids() {
69 + if ( ! class_exists( 'WebberZone\\Top_Ten\\Pro\\Sitewide_Database' ) || ! \WebberZone\Top_Ten\Pro\Sitewide_Database::is_available() ) {
70 + return array();
71 + }
72 +
73 + return \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_ids();
74 + }
75 +
76 + /**
77 + * Build a SQL condition matching any reserved site-wide context ID.
78 + *
79 + * @param string $alias Count-table alias.
80 + * @return string SQL condition, or an always-false condition when Pro is not loaded.
81 + */
82 + protected function get_sitewide_context_condition( $alias = 'ttt' ) {
83 + $ids = $this->get_sitewide_context_ids();
84 + if ( empty( $ids ) ) {
85 + return '0=1';
86 + }
87 +
88 + return sprintf( '%s.postnumber IN (%s)', $alias, implode( ',', array_map( 'intval', $ids ) ) );
89 + }
90 +
91 + /**
92 + * Get the site-wide context key for a result row.
93 + *
94 + * @param array $item Result row.
95 + * @return string Context key, or an empty string for a post row.
96 + */
97 + protected function get_item_context_key( $item ) {
98 + if ( ! isset( $item['ID'] ) || ! class_exists( 'WebberZone\\Top_Ten\\Pro\\Sitewide_Database' ) || ! \WebberZone\Top_Ten\Pro\Sitewide_Database::is_available() ) {
99 + return '';
100 + }
101 +
102 + return \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_key( $item['ID'], $item['blog_id'] ?? null );
103 + }
104 +
105 + /**
106 + * Get a translated label for a site-wide context key.
107 + *
108 + * @param string $context_key Context key.
109 + * @return string Context label.
110 + */
111 + protected function get_sitewide_context_label( $context_key ) {
112 + if ( '' === $context_key || ! class_exists( 'WebberZone\\Top_Ten\\Pro\\Sitewide_Database' ) || ! \WebberZone\Top_Ten\Pro\Sitewide_Database::is_available() ) {
113 + return '';
114 + }
115 +
116 + return \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_label( $context_key );
117 + }
118 +
119 + /**
120 + * Build the search condition for site-wide context labels and keys.
121 + *
122 + * @param string $search Search text.
123 + * @return string SQL condition, or an always-false condition when Pro is not loaded.
124 + */
125 + protected function get_sitewide_context_search_condition( $search ) {
126 + if ( ! class_exists( 'WebberZone\\Top_Ten\\Pro\\Sitewide_Database' ) || ! \WebberZone\Top_Ten\Pro\Sitewide_Database::is_available() ) {
127 + return '0=1';
128 + }
129 +
130 + global $wpdb;
131 +
132 + $like = '%' . $wpdb->esc_like( $search ) . '%';
133 + $conditions = array();
134 + foreach ( \WebberZone\Top_Ten\Pro\Sitewide_Database::CONTEXT_IDS as $context_key => $context_id ) {
135 + $searchable = $context_key . ' ' . $this->get_sitewide_context_label( $context_key );
136 + $conditions[] = $wpdb->prepare( 'ttt.postnumber = %d AND %s LIKE %s', $context_id, $searchable, $like );
137 + }
138 +
139 + return '(' . implode( ' OR ', $conditions ) . ')';
140 + }
141 +
142 + /**
143 + * Resolve a safe ordering clause for statistics queries.
144 + *
145 + * @since 4.5.0
146 + *
147 + * @param array $args Query arguments.
148 + * @return array{orderby:string,order:string} Ordering fields.
149 + */
150 + protected function get_ordering( $args ) {
151 + $orderby = '';
152 + if ( ! empty( $_REQUEST['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
153 + $orderby = sanitize_text_field( wp_unslash( $_REQUEST['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
154 + } elseif ( ! empty( $args['orderby'] ) ) {
155 + $orderby = $args['orderby'];
156 + }
157 +
158 + if ( ! in_array( $orderby, array( 'title', 'daily_count', 'total_count' ), true ) ) {
159 + $orderby = 'total_count';
160 + }
161 +
162 + $order = '';
163 + if ( ! empty( $_REQUEST['order'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
164 + $order = sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
165 + } elseif ( ! empty( $args['order'] ) ) {
166 + $order = $args['order'];
167 + }
168 +
169 + if ( ! in_array( $order, array( 'asc', 'ASC', 'desc', 'DESC' ), true ) ) {
170 + $order = 'DESC';
171 + }
172 +
173 + return array(
174 + 'orderby' => $orderby,
175 + 'order' => strtoupper( $order ),
176 + );
177 + }
178 +
179 + /**
180 + * Fetch network-wide popular posts in two phases.
181 + *
182 + * The first query selects only the requested page in the sort order. The
183 + * second query fills in the other count using exact post/blog pairs, which
184 + * avoids joining and grouping the entire network's count tables.
185 + *
186 + * @since 4.5.0
187 + *
188 + * @param int $per_page Posts per page.
189 + * @param int $page_number Page number.
190 + * @param array $args Query arguments.
191 + * @param string $from_date Inclusive lower date boundary.
192 + * @param string $to_date Exclusive upper date boundary.
193 + * @return array Array of popular posts.
194 + */
195 + protected function get_network_popular_posts( $per_page, $page_number, $args, $from_date, $to_date ) {
196 + global $wpdb;
197 +
198 + $ordering = $this->get_ordering( $args );
199 + $orderby = 'daily_count' === $ordering['orderby'] ? 'daily_count' : 'total_count';
200 + $offset = max( 0, ( (int) $page_number - 1 ) * (int) $per_page );
201 + $limit = max( 0, (int) $per_page );
202 +
203 + if ( 0 === $limit ) {
204 + return array();
205 + }
206 +
207 + $daily_table = $wpdb->base_prefix . 'top_ten_daily';
208 + $total_table = $wpdb->base_prefix . 'top_ten';
209 +
210 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
211 + if ( 'daily_count' === $orderby ) {
212 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
213 + $sql = $wpdb->prepare(
214 + "SELECT postnumber AS ID, blog_id, SUM(cntaccess) AS daily_count
215 + FROM {$daily_table}
216 + WHERE dp_date >= %s AND dp_date < %s
217 + GROUP BY postnumber, blog_id
218 + ORDER BY daily_count {$ordering['order']}, postnumber ASC, blog_id ASC
219 + LIMIT %d, %d",
220 + $from_date,
221 + $to_date,
222 + $offset,
223 + $limit
224 + );
225 + } else {
226 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
227 + $sql = $wpdb->prepare(
228 + "SELECT postnumber AS ID, blog_id, cntaccess AS total_count
229 + FROM {$total_table}
230 + ORDER BY cntaccess {$ordering['order']}, postnumber ASC, blog_id ASC
231 + LIMIT %d, %d",
232 + $offset,
233 + $limit
234 + );
235 + }
236 +
237 + $results = $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
238 + if ( empty( $results ) ) {
239 + return array();
240 + }
241 +
242 + $pair_conditions = array();
243 + foreach ( $results as $result ) {
244 + $pair_conditions[] = $wpdb->prepare( '(postnumber = %d AND blog_id = %d)', $result['ID'], $result['blog_id'] );
245 + }
246 + $pairs = implode( ' OR ', $pair_conditions );
247 +
248 + if ( 'daily_count' === $orderby ) {
249 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
250 + $other_results = $wpdb->get_results(
251 + "SELECT postnumber AS ID, blog_id, cntaccess AS total_count FROM {$total_table} WHERE {$pairs}",
252 + ARRAY_A
253 + ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
254 + } else {
255 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
256 + $other_results = $wpdb->get_results(
257 + $wpdb->prepare(
258 + "SELECT postnumber AS ID, blog_id, SUM(cntaccess) AS daily_count
259 + FROM {$daily_table}
260 + WHERE dp_date >= %s AND dp_date < %s AND ({$pairs})
261 + GROUP BY postnumber, blog_id",
262 + $from_date,
263 + $to_date
264 + ),
265 + ARRAY_A
266 + ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
267 + }
268 +
269 + $other_counts = array();
270 + foreach ( $other_results as $result ) {
271 + $key = (int) $result['ID'] . ':' . (int) $result['blog_id'];
272 + $other_counts[ $key ] = $result[ 'daily_count' === $orderby ? 'total_count' : 'daily_count' ];
273 + }
274 +
275 + foreach ( $results as &$result ) {
276 + $key = (int) $result['ID'] . ':' . (int) $result['blog_id'];
277 + if ( 'daily_count' === $orderby ) {
278 + $result['total_count'] = isset( $other_counts[ $key ] ) ? $other_counts[ $key ] : 0;
279 + } else {
280 + $result['daily_count'] = isset( $other_counts[ $key ] ) ? $other_counts[ $key ] : 0;
281 + }
282 + }
283 + unset( $result );
284 +
285 + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
286 + return $results;
287 + }
288 +
289 + /**
61 290 * Retrieve the Top 10 posts
62 291 *
63 292 * @param int $per_page Posts per page.
64 293 * @param int $page_number Page number.
@@ -68,8 +297,9 @@
68 297 */
69 298 public function get_popular_posts( $per_page = 20, $page_number = 1, $args = null ) {
70 299
71 300 global $wpdb;
301 + $args = is_array( $args ) ? $args : array();
72 302
73 303 // Initialise some variables.
74 304 $fields = array();
75 305 $where = '';
@@ -84,102 +314,91 @@
84 314 $from_date = isset( $args['post-date-filter-from'] ) ? $args['post-date-filter-from'] : current_time( 'd M Y' );
85 315 $from_date = gmdate( 'Y-m-d', strtotime( $from_date ) );
86 316 $to_date = isset( $args['post-date-filter-to'] ) ? $args['post-date-filter-to'] : current_time( 'd M Y' );
87 317 $to_date = gmdate( 'Y-m-d', strtotime( $to_date ) );
318 + $from_date = $from_date . ' 00:00:00';
319 + $to_date = gmdate( 'Y-m-d 00:00:00', strtotime( $to_date . ' +1 day' ) );
88 320
321 + if ( $this->network_wide ) {
322 + $result = $this->get_network_popular_posts( $per_page, $page_number, $args, $from_date, $to_date );
323 +
324 + foreach ( $result as &$row ) {
325 + $row['context_key'] = $this->get_item_context_key( $row );
326 + if ( '' !== $row['context_key'] ) {
327 + $row['title'] = $this->get_sitewide_context_label( $row['context_key'] );
328 + $row['post_type'] = __( 'Site-wide', 'top-10' );
329 + $row['post_date'] = '';
330 + $row['post_author'] = 0;
331 + }
332 + }
333 + unset( $row );
334 +
335 + return $result;
336 + }
337 +
89 338 /* Start creating the SQL */
90 - $table_name_daily = $wpdb->base_prefix . 'top_ten_daily AS ttd';
339 + $table_name_daily = $wpdb->base_prefix . 'top_ten_daily';
91 340 $table_name = $wpdb->base_prefix . 'top_ten AS ttt';
341 + $sitewide_sql = $this->get_sitewide_context_condition();
92 342
93 343 // Fields to return.
94 - if ( ! $this->network_wide ) {
95 - $fields[] = "{$wpdb->posts}.post_title as title";
96 - $fields[] = "{$wpdb->posts}.post_type";
97 - $fields[] = "{$wpdb->posts}.post_date";
98 - $fields[] = "{$wpdb->posts}.post_author";
99 - }
344 + $fields[] = "{$wpdb->posts}.post_title as title";
345 + $fields[] = "{$wpdb->posts}.post_type";
346 + $fields[] = "{$wpdb->posts}.post_date";
347 + $fields[] = "{$wpdb->posts}.post_author";
100 348
101 349 $fields[] = 'ttt.postnumber as ID';
102 350 $fields[] = 'ttt.cntaccess as total_count';
103 - $fields[] = 'SUM(ttd.cntaccess) as daily_count';
351 + $fields[] = 'SUM(ttd.daily_count) as daily_count';
104 352 $fields[] = 'ttt.blog_id as blog_id';
105 353
106 354 $fields = implode( ', ', $fields );
107 355
108 356 // Create the JOIN clause.
109 - if ( ! $this->network_wide ) {
110 - $join .= " LEFT JOIN {$wpdb->posts} ON ttt.postnumber={$wpdb->posts}.ID ";
111 - }
357 + $join .= " LEFT JOIN {$wpdb->posts} ON ttt.postnumber={$wpdb->posts}.ID ";
112 358 $join .= $wpdb->prepare(
113 359 " LEFT JOIN (
114 - SELECT * FROM {$table_name_daily} " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
115 - 'WHERE DATE(ttd.dp_date) >= DATE(%s) AND DATE(ttd.dp_date) <= DATE(%s)
360 + SELECT postnumber, blog_id, SUM(cntaccess) AS daily_count FROM {$table_name_daily} " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
361 + 'WHERE dp_date >= %s AND dp_date < %s AND blog_id = %d
362 + GROUP BY postnumber, blog_id
116 363 ) AS ttd
117 364 ON ttt.postnumber=ttd.postnumber AND ttt.blog_id=ttd.blog_id
118 365 ',
119 366 $from_date,
120 - $to_date
367 + $to_date,
368 + $blog_id
121 369 );
122 370
123 371 // Create the base WHERE clause.
124 - if ( ! $this->network_wide ) {
125 - $where .= $wpdb->prepare( ' AND ttt.blog_id = %d ', $blog_id ); // Posts need to be from the current blog only.
126 - $where .= " AND ($wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status = 'inherit') "; // Show published posts and attachments.
372 + $where .= $wpdb->prepare( ' AND ttt.blog_id = %d ', $blog_id ); // Posts need to be from the current blog only.
373 + $where .= " AND (($wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status = 'inherit') OR {$sitewide_sql}) "; // Show published posts, attachments and site-wide contexts.
127 374
128 - // If search argument is set, do a search for it.
129 - if ( ! empty( $args['search'] ) ) {
130 - $where .= $wpdb->prepare( " AND $wpdb->posts.post_title LIKE %s ", '%' . $wpdb->esc_like( $args['search'] ) . '%' );
131 - }
132 -
133 - // If post filter argument is set, do a search for it.
134 - if ( isset( $args['post-type-filter'] ) && $this->all_post_type['all'] !== $args['post-type-filter'] ) {
135 - $where .= $wpdb->prepare( " AND $wpdb->posts.post_type = %s ", $args['post-type-filter'] );
136 - } else {
137 - $post_types = get_post_types(
138 - array(
139 - 'public' => true,
140 - )
141 - );
142 - $where .= " AND $wpdb->posts.post_type IN ('" . join( "', '", $post_types ) . "') ";
143 - }
375 + // If search argument is set, do a search for it.
376 + if ( ! empty( $args['search'] ) ) {
377 + $post_search = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", '%' . $wpdb->esc_like( $args['search'] ) . '%' );
378 + $context_search = $this->get_sitewide_context_search_condition( $args['search'] );
379 + $where .= " AND ({$post_search} OR {$context_search}) ";
144 380 }
145 381
146 - // Create the base GROUP BY clause.
147 - $groupby = ' ttt.postnumber, ttt.blog_id ';
148 -
149 - // Create the ORDER BY clause.
150 - $orderby = '';
151 - if ( ! empty( $_REQUEST['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
152 - $orderby = sanitize_text_field( wp_unslash( $_REQUEST['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
153 - } elseif ( ! empty( $args['orderby'] ) ) {
154 - $orderby = $args['orderby'];
382 + // If post filter argument is set, do a search for it.
383 + if ( isset( $args['post-type-filter'] ) && $this->all_post_type['all'] !== $args['post-type-filter'] ) {
384 + $where .= $wpdb->prepare( " AND $wpdb->posts.post_type = %s ", $args['post-type-filter'] );
385 + } else {
386 + $post_types = get_post_types(
387 + array(
388 + 'public' => true,
389 + )
390 + );
391 + $where .= " AND ($wpdb->posts.post_type IN ('" . join( "', '", $post_types ) . "') OR {$sitewide_sql}) ";
155 392 }
156 393
157 - if ( $orderby ) {
158 - if ( ! in_array( $orderby, array( 'title', 'daily_count', 'total_count' ) ) ) { //phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
159 - $orderby = ' total_count ';
160 - }
394 + $ordering = $this->get_ordering( $args );
395 + $orderby = $ordering['orderby'] . ' ' . $ordering['order'];
161 396
162 - $order = '';
163 - if ( ! empty( $_REQUEST['order'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
164 - $order = sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
165 - } elseif ( ! empty( $args['order'] ) ) {
166 - $order = $args['order'];
167 - }
168 -
169 - if ( $order && in_array( $order, array( 'asc', 'ASC', 'desc', 'DESC' ), true ) ) {
170 - $orderby .= " {$order}";
171 - } else {
172 - $orderby .= ' DESC';
173 - }
174 - } else {
175 - $orderby = ' total_count DESC ';
176 - }
177 -
178 397 // Create the base LIMITS clause.
179 398 $limits = $wpdb->prepare( ' LIMIT %d, %d ', ( $page_number - 1 ) * $per_page, $per_page );
180 399
181 - $groupby = " GROUP BY {$groupby} ";
400 + $groupby = ' GROUP BY ttt.postnumber, ttt.blog_id ';
182 401 $orderby = " ORDER BY {$orderby} ";
183 402
184 403 $sql = "SELECT $fields FROM {$table_name} $join WHERE 1=1 $where $groupby $orderby $limits";
185 404
@@ -184,8 +403,19 @@
184 403 $sql = "SELECT $fields FROM {$table_name} $join WHERE 1=1 $where $groupby $orderby $limits";
185 404
186 405 $result = $wpdb->get_results( $sql, 'ARRAY_A' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
187 406
407 + foreach ( $result as &$row ) {
408 + $row['context_key'] = $this->get_item_context_key( $row );
409 + if ( '' !== $row['context_key'] ) {
410 + $row['title'] = $this->get_sitewide_context_label( $row['context_key'] );
411 + $row['post_type'] = __( 'Site-wide', 'top-10' );
412 + $row['post_date'] = '';
413 + $row['post_author'] = 0;
414 + }
415 + }
416 + unset( $row );
417 +
188 418 return $result;
189 419 }
190 420
191 421 /**
@@ -196,32 +426,52 @@
196 426 */
197 427 public function record_count( $args = null ) {
198 428
199 429 global $wpdb;
430 + $args = is_array( $args ) ? $args : array();
200 431
432 + if ( $this->network_wide ) {
433 + $cache_key = 'tptn_network_popular_posts_count';
434 + $cached = get_site_transient( $cache_key );
435 + if ( false !== $cached ) {
436 + return (string) (int) $cached;
437 + }
438 +
439 + $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->base_prefix}top_ten" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
440 + /** This filter is documented in includes/admin/class-dashboard-widgets.php */
441 + $ttl = max( 0, (int) apply_filters( 'tptn_network_dashboard_cache_ttl', 15 * MINUTE_IN_SECONDS ) );
442 + if ( $ttl > 0 ) {
443 + set_site_transient( $cache_key, $count, $ttl );
444 + }
445 +
446 + return (string) $count;
447 + }
448 +
201 449 $where = '';
202 450 $join = '';
203 451
204 452 $sql = "SELECT COUNT(*) FROM {$wpdb->base_prefix}top_ten as ttt";
205 453
206 - if ( ! $this->network_wide ) {
207 - $join = "INNER JOIN {$wpdb->posts} ON ttt.postnumber=ID";
208 - $where .= $wpdb->prepare( ' AND blog_id = %d ', get_current_blog_id() );
454 + $join = "LEFT JOIN {$wpdb->posts} ON ttt.postnumber={$wpdb->posts}.ID";
455 + $sitewide_sql = $this->get_sitewide_context_condition();
456 + $where .= $wpdb->prepare( ' AND ttt.blog_id = %d ', get_current_blog_id() );
457 + $where .= " AND (($wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status = 'inherit') OR {$sitewide_sql}) ";
209 458
210 - if ( ! empty( $args['search'] ) ) {
211 - $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_title LIKE %s ", '%' . $wpdb->esc_like( $args['search'] ) . '%' );
212 - }
459 + if ( ! empty( $args['search'] ) ) {
460 + $post_search = $wpdb->prepare( "{$wpdb->posts}.post_title LIKE %s", '%' . $wpdb->esc_like( $args['search'] ) . '%' );
461 + $context_search = $this->get_sitewide_context_search_condition( $args['search'] );
462 + $where .= " AND ({$post_search} OR {$context_search}) ";
463 + }
213 464
214 - if ( isset( $args['post-type-filter'] ) && $this->all_post_type['all'] !== $args['post-type-filter'] ) {
215 - $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s ", $args['post-type-filter'] );
216 - } else {
217 - $post_types = get_post_types(
218 - array(
219 - 'public' => true,
220 - )
221 - );
222 - $where .= " AND $wpdb->posts.post_type IN ('" . join( "', '", $post_types ) . "') ";
223 - }
465 + if ( isset( $args['post-type-filter'] ) && $this->all_post_type['all'] !== $args['post-type-filter'] ) {
466 + $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s ", $args['post-type-filter'] );
467 + } else {
468 + $post_types = get_post_types(
469 + array(
470 + 'public' => true,
471 + )
472 + );
473 + $where .= " AND ($wpdb->posts.post_type IN ('" . join( "', '", $post_types ) . "') OR {$sitewide_sql}) ";
224 474 }
225 475 return $wpdb->get_var( "$sql $join WHERE 1=1 $where" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
226 476 }
227 477
@@ -283,8 +533,17 @@
283 533 * @param array $item Current item.
284 534 * @return string
285 535 */
286 536 public function column_title( $item ) {
537 + $context_key = $this->get_item_context_key( $item );
538 + if ( '' !== $context_key ) {
539 + return sprintf(
540 + '<strong>%1$s</strong> <span style="color:grey">(%2$s, id:%3$s)</span>',
541 + esc_html( $this->get_sitewide_context_label( $context_key ) ),
542 + esc_html__( 'site-wide', 'top-10' ),
543 + esc_html( $item['ID'] )
544 + );
545 + }
287 546
288 547 $delete_nonce = wp_create_nonce( 'tptn_delete_entry' );
289 548 $page = isset( $_REQUEST['page'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
290 549 $post = $this->network_wide ? get_blog_post( $item['blog_id'], $item['ID'] ) : get_post( $item['ID'] );
@@ -327,8 +586,11 @@
327 586 * @param array $item Current item.
328 587 * @return string Post date.
329 588 */
330 589 public function column_date( $item ) {
590 + if ( '' !== $this->get_item_context_key( $item ) ) {
591 + return '';
592 + }
331 593
332 594 $post = is_multisite() ? get_blog_post( $item['blog_id'], $item['ID'] ) : get_post( $item['ID'] );
333 595
334 596 if ( $post ) {
@@ -349,8 +611,11 @@
349 611 * @param array $item Current item.
350 612 * @return string Post Type.
351 613 */
352 614 public function column_post_type( $item ) {
615 + if ( '' !== $this->get_item_context_key( $item ) ) {
616 + return esc_html__( 'Site-wide', 'top-10' );
617 + }
353 618
354 619 $post = is_multisite() ? get_blog_post( $item['blog_id'], $item['ID'] ) : get_post( $item['ID'] );
355 620
356 621 if ( $post ) {
@@ -370,8 +635,11 @@
370 635 * @param array $item Current item.
371 636 * @return string Post Author.
372 637 */
373 638 public function column_author( $item ) {
639 + if ( '' !== $this->get_item_context_key( $item ) ) {
640 + return '';
641 + }
374 642
375 643 $post = is_multisite() ? get_blog_post( $item['blog_id'], $item['ID'] ) : get_post( $item['ID'] );
376 644 if ( ! $post ) {
377 645 return '';
@@ -377,9 +645,9 @@
377 645 return '';
378 646 }
379 647
380 648 $author_info = get_userdata( (int) $post->post_author );
381 - $author_name = ( false === $author_info ) ? '' : ucwords( trim( stripslashes( $author_info->display_name ) ) );
649 + $author_name = ( false === $author_info ) ? '' : ucwords( trim( stripslashes( $author_info->display_name ), " \t\n\r\0\x0B" ) );
382 650
383 651 return sprintf(
384 652 '<a href="%s">%s</a>',
385 653 esc_url(
@@ -401,8 +669,12 @@
401 669 * @param array $item Current item.
402 670 * @return string
403 671 */
404 672 public function column_total_count( $item ) {
673 + if ( '' !== $this->get_item_context_key( $item ) ) {
674 + return \WebberZone\Top_Ten\Util\Helpers::number_format_i18n( absint( $item['total_count'] ) );
675 + }
676 +
405 677 return sprintf(
406 678 '<div contentEditable="true" class="live_edit" id="total_count_%1$s" data-wp-post-id="%1$s" data-wp-count="%2$s"%3$s>%4$s</div>',
407 679 $item['ID'],
408 680 $item['total_count'],