PluginProbe
Code Snippets / 3.9.1
Code Snippets v3.9.1
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.9.1, at php/cloud/class-cloud-search-list-table.php

354 lines 11.4 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 cloud search results table
4 *
5 * @package Code_Snippets
6 */
7
8 namespace Code_Snippets\Cloud;
9
10 use WP_Plugin_Install_List_Table;
11 use function Code_Snippets\code_snippets;
12
13 if ( ! class_exists( 'WP_Plugin_Install_List_Table' ) ) {
14 require_once ABSPATH . 'wp-admin/includes/class-wp-plugin-install-list-table.php';
15 }
16
17 /**
18 * Class for handling the cloud search results table.
19 *
20 * @property string $_pagination Pagination HTML.
21 *
22 * @package Code_Snippets
23 */
24 class Cloud_Search_List_Table extends WP_Plugin_Install_List_Table {
25
26 /**
27 * Instance of Cloud API class.
28 *
29 * @var Cloud_API
30 */
31 protected Cloud_API $cloud_api;
32
33 /**
34 * Items for the cloud list table.
35 *
36 * @var Cloud_Snippets
37 */
38 protected Cloud_Snippets $cloud_snippets;
39
40 /**
41 * Class constructor.
42 */
43 public function __construct() {
44 /**
45 * Declare global variable due to undeclared warning.
46 *
47 * @noinspection PhpUnusedLocalVariableInspection
48 */
49 global $tab;
50
51 parent::__construct(
52 [
53 'singular' => 'cloud-snippet',
54 'plural' => 'cloud-snippets',
55 'ajax' => false,
56 ]
57 );
58
59 // Strip the result query arg from the URL.
60 $_SERVER['REQUEST_URI'] = remove_query_arg( [ 'result' ] );
61
62 $this->cloud_api = code_snippets()->cloud_api;
63 }
64
65 /**
66 * Prepare items for the table.
67 *
68 * @return void
69 */
70 public function prepare_items() {
71 $this->cloud_snippets = $this->fetch_snippets();
72 $this->items = $this->cloud_snippets->snippets;
73
74 $this->process_actions();
75
76 $this->set_pagination_args(
77 [
78 'per_page' => count( $this->cloud_snippets->snippets ),
79 'total_items' => $this->cloud_snippets->total_snippets,
80 'total_pages' => $this->cloud_snippets->total_pages,
81 ]
82 );
83 }
84
85 /**
86 * Process any actions that have been submitted, such as downloading cloud snippets to the local database.
87 *
88 * @return void
89 */
90 public function process_actions() {
91 $_SERVER['REQUEST_URI'] = remove_query_arg(
92 [ 'action', 'snippet', '_wpnonce', 'source', 'cloud-bundle-run', 'cloud-bundle-show', 'bundle_share_name', 'cloud_bundles' ]
93 );
94
95 // Check request is coming form the cloud search page.
96 if ( isset( $_REQUEST['type'] ) && 'cloud_search' === $_REQUEST['type'] ) {
97 if ( isset( $_REQUEST['action'], $_REQUEST['snippet'], $_REQUEST['source'] ) ) {
98 cloud_lts_process_download_action(
99 sanitize_key( wp_unslash( $_REQUEST['action'] ) ),
100 sanitize_key( wp_unslash( $_REQUEST['source'] ) ),
101 sanitize_key( wp_unslash( $_REQUEST['snippet'] ) )
102 );
103 }
104 }
105 }
106
107 /**
108 * Output table rows.
109 *
110 * @return void
111 */
112 public function display_rows() {
113 $status_descriptions = [
114 Cloud_API::STATUS_PUBLIC =>
115 __( 'Snippet has passed basic review.', 'code-snippets' ),
116 Cloud_API::STATUS_AI_VERIFIED =>
117 __( 'Snippet has been tested by our AI bot.', 'code-snippets' ),
118 Cloud_API::STATUS_UNVERIFIED =>
119 __( 'Snippet has not undergone any review yet.', 'code-snippets' ),
120 ];
121
122 /**
123 * The current table item.
124 *
125 * @var $item Cloud_Snippet
126 */
127 foreach ( $this->items as $item ) {
128 ?>
129 <div class="plugin-card cloud-search-card plugin-card-<?php echo esc_attr( $item->id ); ?>">
130 <?php
131 cloud_lts_display_column_hidden_input( 'code', $item );
132 cloud_lts_display_column_hidden_input( 'name', $item );
133 ?>
134 <div class="plugin-card-top">
135 <div class="name column-name">
136 <h3>
137 <?php
138 $link = code_snippets()->cloud_api->get_link_for_cloud_snippet( $item );
139
140 if ( $link ) {
141 printf( '<a href="%s">', esc_url( code_snippets()->get_snippet_edit_url( $link->local_id ) ) );
142 } else {
143 printf(
144 '<a href="%s" title="%s" class="cloud-snippet-preview thickbox" data-snippet="%s" data-lang="%s">',
145 '#TB_inline?&width=700&height=500&inlineId=show-code-preview',
146 esc_attr__( 'Preview this snippet', 'code-snippets' ),
147 esc_attr( $item->id ),
148 esc_attr( Cloud_API::get_type_from_scope( $item->scope ) )
149 );
150 }
151
152 echo esc_html( $item->name );
153
154 // Grab first tag in array of tags.
155 $category = count( $item->tags ) > 0 ? strtolower( esc_attr( $item->tags[0] ) ) : 'general';
156
157 printf(
158 '<img src="%s" class="plugin-icon" alt="%s">',
159 esc_url( "https://codesnippets.cloud/images/plugin-icons/$category-logo.png" ),
160 esc_attr( $category )
161 );
162
163 echo '</a>';
164 ?>
165 </h3>
166 </div>
167 <div class="action-links">
168 <ul class="plugin-action-buttons">
169 <?php cloud_lts_render_action_buttons( $item, 'search' ); ?>
170 </ul>
171 </div>
172 <div class="desc column-description">
173 <p><?php echo wp_kses_post( $this->process_description( $item->description ) ); ?></p>
174 <p class="authors">
175 <cite>
176 <?php
177 printf(
178 '%s <a target="_blank" href="%s">%s</a>',
179 esc_html__( 'Codevault:', 'code-snippets' ),
180 esc_url( sprintf( 'https://codesnippets.cloud/codevault/%s', $item->codevault ) ),
181 esc_html( $item->codevault )
182 );
183 ?>
184 </cite>
185 </p>
186 </div>
187 </div>
188 <div class="plugin-card-bottom cloud-search-card-bottom">
189 <div class="vers column-rating voted-info">
190 <svg xmlns="http://www.w3.org/2000/svg"
191 fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="thumbs-up">
192 <path stroke-linecap="round" stroke-linejoin="round"
193 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>
194 </svg>
195 <span class="num-ratings" aria-hidden="true">
196 <?php
197 // translators: 1: number of votes.
198 $votes_text = _nx( '%d time', '%d times', $item->vote_count, 'vote count', 'code-snippets' );
199 $votes_text = sprintf( $votes_text, number_format_i18n( $item->vote_count ) );
200
201 // translators: 1: number of users.
202 $users_text = _n( '%d user', '%d users', $item->total_votes, 'code-snippets' );
203 $users_text = sprintf( $users_text, number_format_i18n( $item->total_votes ) );
204
205 // translators: 1: number of votes with label, 2: number of users with label.
206 echo esc_html( sprintf( _x( '%1$s by %2$s', 'votes', 'code-snippets' ), $votes_text, $users_text ) );
207 ?>
208 </span>
209 </div>
210 <div class="column-updated">
211 <strong><?php esc_html_e( 'Last Updated:', 'code-snippets' ); ?></strong>
212 <?php
213 // translators: %s: Human-readable time difference.
214 echo esc_html( sprintf( __( '%s ago', 'code-snippets' ), human_time_diff( strtotime( $item->updated ) ) ) );
215 ?>
216 </div>
217 <div class="column-downloaded">
218 <div class="badge <?php echo esc_attr( $this->cloud_api->get_status_badge( $item->status ) ); ?>-badge tooltip tooltip-block tooltip-end">
219 <?php
220
221 echo esc_html( $this->cloud_api->get_status_label( $item->status ) );
222
223 if ( isset( $status_descriptions[ $item->status ] ) ) {
224 echo '<span class="dashicons dashicons-info-outline"></span>';
225 printf( '<div class="tooltip-content">%s</div>', esc_html( $status_descriptions[ $item->status ] ) );
226 }
227 ?>
228 </div>
229 </div>
230 <div class="column-compatibility">
231 <strong><?php esc_html_e( 'WP Compatibility:', 'code-snippets' ); ?></strong>
232 <?php
233 if ( empty( $wp_tested ) ) {
234 printf(
235 '<span class="compatibility-untested">%s</span>',
236 esc_html__( 'Not indicated by author', 'code-snippets' )
237 );
238 } else {
239 printf(
240 '<span class="compatibility-compatible">%s</span>',
241 // translators: %s: tested status.
242 esc_html( sprintf( __( 'Author states %s', 'code-snippets' ), $wp_tested ) )
243 );
244 }
245 ?>
246 </div>
247 </div>
248 </div>
249 <?php
250 }
251 }
252
253 /**
254 * Process the description text - limit to 150 characters.
255 *
256 * @param string|null $description Description as provided by the API.
257 *
258 * @return string formatted description string max 150 chars.
259 */
260 protected function process_description( ?string $description ): string {
261 $description = wp_strip_all_tags( $description );
262 return strlen( $description ) > 150 ? substr( $description, 0, 150 ) . '…' : $description;
263 }
264
265 /**
266 * Text displayed when no snippet data is available.
267 *
268 * @return void
269 */
270 public function no_items() {
271 if ( ! empty( $_REQUEST['cloud_search'] ) && count( $this->cloud_snippets->snippets ) < 1 ) {
272 echo '<p class="no-results">',
273 esc_html__( 'No snippets or codevault could be found with that search term. Please try again.', 'code-snippets' ),
274 '</p>';
275 } else {
276 echo '<p>', esc_html__( 'Please enter a term to start searching code snippets in the cloud.', 'code-snippets' ), '</p>';
277 }
278 }
279
280 /**
281 * Fetch the snippets used to populate the table.
282 *
283 * @return Cloud_Snippets
284 */
285 public function fetch_snippets(): Cloud_Snippets {
286 // Check if search term has been entered.
287 if ( isset( $_REQUEST['type'], $_REQUEST['cloud_search'], $_REQUEST['cloud_select'] ) &&
288 'cloud_search' === sanitize_key( wp_unslash( $_REQUEST['type'] ) )
289 ) {
290 // If we have a search query, then send a search request to cloud server API search endpoint.
291 $search_query = sanitize_text_field( wp_unslash( $_REQUEST['cloud_search'] ) );
292 $search_by = sanitize_text_field( wp_unslash( $_REQUEST['cloud_select'] ) );
293
294 return Cloud_API::fetch_search_results( $search_by, $search_query, $this->get_pagenum() - 1 );
295 }
296
297 // If no search results, then return empty object.
298 return new Cloud_Snippets();
299 }
300
301 /**
302 * Gets the current search result page number.
303 *
304 * @return integer
305 */
306 public function get_pagenum(): int {
307 $page = isset( $_REQUEST['search_page'] ) ? absint( $_REQUEST['search_page'] ) : 0;
308
309 if ( isset( $this->_pagination_args['total_pages'] ) && $page > $this->_pagination_args['total_pages'] ) {
310 $page = $this->_pagination_args['total_pages'];
311 }
312
313 return max( 1, $page );
314 }
315
316 /**
317 * Display the table.
318 *
319 * @return void
320 */
321 public function display() {
322 Cloud_API::render_cloud_snippet_thickbox();
323 parent::display();
324 }
325
326 /**
327 * Displays the pagination.
328 *
329 * @param string $which Context where the pagination will be displayed.
330 *
331 * @return void
332 */
333 protected function pagination( $which ) {
334 $total_items = $this->_pagination_args['total_items'];
335 $total_pages = $this->_pagination_args['total_pages'];
336 $pagenum = $this->get_pagenum();
337
338 if ( 'top' === $which && $total_pages > 1 ) {
339 $this->screen->render_screen_reader_content( 'heading_pagination' );
340 }
341
342 $paginate = cloud_lts_pagination( $which, 'search', $total_items, $total_pages, $pagenum );
343 $page_class = $paginate['page_class'];
344 $output = $paginate['output'];
345
346 $this->_pagination = "<div class='tablenav-pages$page_class'>$output</div>";
347
348 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
349 echo $this->_pagination;
350
351 // echo wp_kses_post( $this->_pagination ); TODO: This removes the top input box for page number.
352 }
353 }
354