PluginProbe
Code Snippets / 3.6.6
Code Snippets v3.6.6
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / php / cloud / class-cloud-search-list-table.php

class-cloud-search-list-table.php in Code Snippets 3.6.6, at php/cloud/class-cloud-search-list-table.php

378 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Contains the class for handling the snippets table
4 *
5 * @package Code_Snippets
6 *
7 * phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
8 */
9
10 namespace Code_Snippets\Cloud;
11
12 use WP_Plugin_Install_List_Table;
13 use function Code_Snippets\code_snippets;
14
15 if ( ! class_exists( 'WP_Plugin_Install_List_Table' ) ) {
16 require_once ABSPATH . 'wp-admin/includes/class-wp-plugin-install-list-table.php';
17 }
18
19 /**
20 * Class for handling the cloud search results table.
21 *
22 * @property string $_pagination Pagination HTML.
23 *
24 * @package Code_Snippets
25 */
26 class Cloud_Search_List_Table extends WP_Plugin_Install_List_Table {
27
28 /**
29 * Instance of Cloud API class.
30 *
31 * @var Cloud_API
32 */
33 protected Cloud_API $cloud_api;
34
35 /**
36 * Items for the cloud list table.
37 *
38 * @var Cloud_Snippets
39 */
40 protected Cloud_Snippets $cloud_snippets;
41
42 /**
43 * Class constructor.
44 */
45 public function __construct() {
46 // Declare global variable due to undeclared warning.
47 global $tab;
48
49 parent::__construct(
50 [
51 'singular' => 'cloud-snippet',
52 'plural' => 'cloud-snippets',
53 'ajax' => false,
54 ]
55 );
56
57 // Strip the result query arg from the URL.
58 $_SERVER['REQUEST_URI'] = remove_query_arg( [ 'result' ] );
59
60 $this->cloud_api = code_snippets()->cloud_api;
61 }
62
63 /**
64 * Prepare items for the table.
65 *
66 * @return void
67 */
68 public function prepare_items() {
69 $this->cloud_snippets = $this->fetch_snippets();
70 $this->items = $this->cloud_snippets->snippets;
71
72 $this->process_actions();
73
74 $this->set_pagination_args(
75 [
76 'per_page' => count( $this->cloud_snippets->snippets ),
77 'total_items' => $this->cloud_snippets->total_snippets,
78 'total_pages' => $this->cloud_snippets->total_pages,
79 ]
80 );
81 }
82
83 /**
84 * Process any actions that have been submitted, such as downloading cloud snippets to the local database.
85 *
86 * @return void
87 */
88 public function process_actions() {
89 $_SERVER['REQUEST_URI'] = remove_query_arg(
90 [ 'action', 'snippet', '_wpnonce', 'source', 'cloud-bundle-run', 'cloud-bundle-show', 'bundle_share_name', 'cloud_bundles' ]
91 );
92
93 if ( isset( $_REQUEST['action'], $_REQUEST['snippet'], $_REQUEST['source'] ) ) {
94 cloud_lts_process_download_action(
95 sanitize_key( wp_unslash( $_REQUEST['action'] ) ),
96 sanitize_key( wp_unslash( $_REQUEST['source'] ) ),
97 sanitize_key( wp_unslash( $_REQUEST['snippet'] ) )
98 );
99 }
100 }
101
102 /**
103 * Output table rows.
104 *
105 * @return void
106 */
107 public function display_rows() {
108 /**
109 * The current table item.
110 *
111 * @var $item Cloud_Snippet
112 */
113 foreach ( $this->items as $item ) {
114 ?>
115 <div class="plugin-card cloud-search-card plugin-card-<?php echo sanitize_html_class( $item->id ); ?>">
116 <?php
117 cloud_lts_display_column_hidden_input( 'code', $item );
118 cloud_lts_display_column_hidden_input( 'name', $item );
119 ?>
120 <div class="plugin-card-top">
121 <div class="name column-name">
122 <h3>
123 <?php
124 $name_link = $this->get_link_for_name();
125
126 if ( $name_link['cloud-snippet-downloaded'] ) {
127 printf( '<a href="%s">', esc_url( $name_link['cloud-snippet-link'] ) );
128 } else {
129 printf(
130 '<a href="%s" title="%s" class="cloud-snippet-preview thickbox" data-snippet="%s" data-lang="%s">',
131 esc_url( $name_link['cloud-snippet-link'] ),
132 esc_attr__( 'Preview this snippet', 'code-snippets' ),
133 esc_attr( $item->id ),
134 esc_attr( Cloud_API::get_type_from_scope( $item->scope ) )
135 );
136 }
137
138 echo esc_html( $item->name );
139
140 // Grab first tag in array of tags.
141 $category = count( $item->tags ) > 0 ? strtolower( esc_attr( $item->tags[0] ) ) : '';
142
143 printf(
144 '<img src="%s" class="plugin-icon" alt="%s">',
145 esc_url( "https://codesnippets.cloud/images/plugin-icons/$category-logo.png" ),
146 esc_attr( $category )
147 );
148
149 echo '</a>';
150 ?>
151 </h3>
152 </div>
153 <div class="action-links">
154 <?php echo wp_kses_post( cloud_lts_build_action_links( $item, 'search' ) ); ?>
155 </div>
156 <div class="desc column-description">
157 <p><?php wp_kses_post( $this->process_description( $item->description ) ); ?></p>
158 <p class="authors">
159 <cite>
160 <?php
161 esc_html_e( 'Codevault: ', 'code-snippets' );
162
163 printf(
164 '<a target="_blank" href="%s">%s</a>',
165 esc_url( sprintf( 'https://codesnippets.cloud/codevault/%s', $item->codevault ) ),
166 esc_html( $item->codevault )
167 );
168
169 ?>
170 </cite>
171 </p>
172 </div>
173 </div>
174 <div class="plugin-card-bottom cloud-search-card-bottom">
175 <div class="vers column-rating voted-info">
176 <svg xmlns="http://www.w3.org/2000/svg"
177 fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="thumbs-up">
178 <path stroke-linecap="round" stroke-linejoin="round"
179 d="M6.633 10.5c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75A2.25 2.25 0 0116.5 4.5c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23H5.904M14.25 9h2.25M5.904 18.75c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 01-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 10.203 4.167 9.75 5 9.75h1.053c.472 0 .745.556.5.96a8.958 8.958 0 00-1.302 4.665c0 1.194.232 2.333.654 3.375z"></path>
180 </svg>
181 <span class="num-ratings" aria-hidden="true">
182 <?php
183 // translators: 1: number of votes.
184 $votes_text = _nx( '%d time', '%d times', $item->vote_count, 'vote count', 'code-snippets' );
185 $votes_text = sprintf( $votes_text, number_format_i18n( $item->vote_count ) );
186
187 // translators: 1: number of users.
188 $users_text = _n( '%d user', '%d users', $item->total_votes, 'code-snippets' );
189 $users_text = sprintf( $users_text, number_format_i18n( $item->total_votes ) );
190
191 // translators: 1: number of votes with label, 2: number of users with label.
192 echo esc_html( sprintf( _x( '%1$s by %2$s', 'votes', 'code-snippets' ), $votes_text, $users_text ) );
193 ?>
194 </span>
195 </div>
196 <div class="column-updated">
197 <strong><?php esc_html_e( 'Last Updated:', 'code-snippets' ); ?></strong>
198 <?php
199 // translators: %s: Human-readable time difference.
200 echo esc_html( sprintf( __( '%s ago', 'code-snippets' ), human_time_diff( strtotime( $item->updated ) ) ) );
201 ?>
202 </div>
203 <div class="column-downloaded">
204 <?php
205 $status_name = $this->cloud_api->get_status_name_from_status( $item->status );
206
207 printf(
208 '<a class="snippet-type-badge snippet-status" data-type="%s">%s</a>',
209 esc_attr( sanitize_title_with_dashes( strtolower( $status_name ) ) ),
210 esc_html( $status_name )
211 );
212 ?>
213 <div class="tooltip-box">
214 <span class="dashicons dashicons-editor-help"></span>
215 <div class="tooltip-text">
216 <p class="tooltip-text-title">
217 <?php esc_html_e( 'Snippet Statuses', 'code-snippets' ); ?>
218 </p>
219
220 <p class="tooltip-text-item">
221 <a class="snippet-type-badge snippet-status" data-type="public">
222 <?php esc_html_e( 'Public', 'code-snippets' ); ?></a>
223 <?php esc_html_e( 'Snippet has passed basic review.', 'code-snippets' ); ?>
224 </p>
225
226 <p class="tooltip-text-item">
227 <a class="snippet-type-badge snippet-status" data-type="ai-verified">
228 <?php esc_html_e( 'AI Verified', 'code-snippets' ); ?></a>
229 <?php esc_html_e( ' Snippet has been tested by our AI bot.', 'code-snippets' ); ?>
230 </p>
231
232 <p class="tooltip-text-item">
233 <a class="snippet-type-badge snippet-status" data-type="unverified">
234 <?php esc_html_e( 'Unverified', 'code-snippets' ); ?></a>
235 <?php esc_html_e( ' Snippet has not undergone any review yet.', 'code-snippets' ); ?>
236 </p>
237
238 <p class="tooltip-text-link">
239 <a class="tooltip-text-link"
240 href="https://codesnippets.cloud/getstarted"
241 target="_blankx">
242 <?php esc_html_e( 'View the full list.', 'code-snippets' ); ?></a>
243 </p>
244 </div>
245 </div>
246 </div>
247 <div class="column-compatibility">
248 <strong><?php esc_html_e( 'WP Compatability:', 'code-snippets' ); ?></strong>
249 <?php
250 if ( empty( $wp_tested ) ) {
251 echo '<span class="compatibility-untested">', esc_html__( 'Not indicated by author', 'code-snippets' ), '</span>';
252 } else {
253 // translators: tested status.
254 $text = sprintf( __( 'Author states %s', 'code-snippets' ), $wp_tested );
255 echo '<span class="compatibility-compatible">', esc_html( $text ), '</span>';
256 }
257 ?>
258 </div>
259 </div>
260 </div>
261 <?php
262 }
263 }
264
265 /**
266 * Define the url for the name anchor tag
267 *
268 * @return array The URL to be used.
269 */
270 protected function get_link_for_name(): array {
271 return [
272 'cloud-snippet-link' => '#TB_inline?&width=700&height=500&inlineId=show-code-preview',
273 'cloud-snippet-downloaded' => false,
274 ];
275 }
276
277 /**
278 * Process the description text - limit to 150 characters.
279 *
280 * @param string|null $description Description as provided by the API.
281 *
282 * @return string formatted description string max 150 chars.
283 */
284 protected function process_description( ?string $description ): string {
285 $description = wp_strip_all_tags( $description );
286 return strlen( $description ) > 150 ? substr( $description, 0, 150 ) . '…' : $description;
287 }
288
289 /**
290 * Text displayed when no snippet data is available.
291 *
292 * @return void
293 */
294 public function no_items() {
295 if ( ! empty( $_REQUEST['cloud_search'] ) && count( $this->cloud_snippets->snippets ) < 1 ) {
296 echo '<p class="no-results">',
297 esc_html__( 'No snippets or codevault could be found with that search term. Please try again.', 'code-snippets' ),
298 '</p>';
299 } else {
300 echo '<p>', esc_html__( 'Please enter a term to start searching code snippets in the cloud.', 'code-snippets' ), '</p>';
301 }
302 }
303
304 /**
305 * Fetch the snippets used to populate the table.
306 *
307 * @return Cloud_Snippets
308 */
309 public function fetch_snippets(): Cloud_Snippets {
310 // Check if search term has been entered.
311 if ( isset( $_REQUEST['type'], $_REQUEST['cloud_search'], $_REQUEST['cloud_select'] ) &&
312 'cloud_search' === sanitize_key( wp_unslash( $_REQUEST['type'] ) )
313 ) {
314 // If we have a search query, then send a search request to cloud server API search endpoint.
315 $search_query = sanitize_text_field( wp_unslash( $_REQUEST['cloud_search'] ) );
316 $search_by = sanitize_text_field( wp_unslash( $_REQUEST['cloud_select'] ) );
317
318 return Cloud_API::fetch_search_results( $search_by, $search_query, $this->get_pagenum() - 1 );
319 }
320
321 // If no search results, then return empty object.
322 return new Cloud_Snippets();
323 }
324
325 /**
326 * Gets the current search result page number.
327 *
328 * @return integer
329 */
330 public function get_pagenum(): int {
331 $page = isset( $_REQUEST['search_page'] ) ? absint( $_REQUEST['search_page'] ) : 0;
332
333 if ( isset( $this->_pagination_args['total_pages'] ) && $page > $this->_pagination_args['total_pages'] ) {
334 $page = $this->_pagination_args['total_pages'];
335 }
336
337 return max( 1, $page );
338 }
339
340 /**
341 * Display the table.
342 *
343 * @return void
344 */
345 public function display() {
346 Cloud_API::render_cloud_snippet_thickbox();
347 parent::display();
348 }
349
350 /**
351 * Displays the pagination.
352 *
353 * @param string $which Context where the pagination will be displayed.
354 *
355 * @return void
356 */
357 protected function pagination( $which ) {
358 $total_items = $this->_pagination_args['total_items'];
359 $total_pages = $this->_pagination_args['total_pages'];
360 $pagenum = $this->get_pagenum();
361
362 if ( 'top' === $which && $total_pages > 1 ) {
363 $this->screen->render_screen_reader_content( 'heading_pagination' );
364 }
365
366 $paginate = cloud_lts_pagination( $which, 'search', $total_items, $total_pages, $pagenum );
367 $page_class = $paginate['page_class'];
368 $output = $paginate['output'];
369
370 $this->_pagination = "<div class='tablenav-pages$page_class'>$output</div>";
371
372 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
373 echo $this->_pagination;
374
375 // echo wp_kses_post( $this->_pagination ); TODO: This removes the top input box for page number.
376 }
377 }
378