Skip to content

Commit c6f7141

Browse files
committed
fix: improve pagination and snippet fetching in cloud search list table
1 parent 0b15874 commit c6f7141

File tree

3 files changed

+120
-103
lines changed

3 files changed

+120
-103
lines changed

src/php/cloud/class-cloud-search-list-table.php

Lines changed: 76 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,22 @@ public function __construct() {
6868
* @return void
6969
*/
7070
public function prepare_items() {
71-
$this->cloud_snippets = $this->fetch_snippets();
71+
$per_page = $this->get_items_per_page( 'snippets_per_page', 10 );
72+
$user_per_page = (int) get_user_option( 'snippets_per_page', get_current_user_id() );
73+
if ( $user_per_page > 0 ) {
74+
$per_page = $user_per_page;
75+
}
76+
77+
// Fetch snippets, passing a 0-based page index to the Cloud API (WP list tables are 1-based).
78+
$page_index = max( 0, $this->get_pagenum() - 1 );
79+
$this->cloud_snippets = $this->fetch_snippets( $per_page, $page_index );
7280
$this->items = $this->cloud_snippets->snippets;
7381

7482
$this->process_actions();
7583

7684
$this->set_pagination_args(
7785
[
78-
'per_page' => count( $this->cloud_snippets->snippets ),
86+
'per_page' => $per_page,
7987
'total_items' => $this->cloud_snippets->total_snippets,
8088
'total_pages' => $this->cloud_snippets->total_pages,
8189
]
@@ -94,13 +102,14 @@ public function process_actions() {
94102

95103
// Check request is coming form the cloud search page.
96104
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-
}
105+
if ( isset( $_REQUEST['action'], $_REQUEST['snippet'], $_REQUEST['source'] ) ) {
106+
cloud_lts_process_download_action(
107+
sanitize_key( wp_unslash( $_REQUEST['action'] ) ),
108+
sanitize_key( wp_unslash( $_REQUEST['source'] ) ),
109+
sanitize_key( wp_unslash( $_REQUEST['snippet'] ) ),
110+
$this->get_pagenum()
111+
);
112+
}
104113
}
105114
}
106115

@@ -126,15 +135,25 @@ public function display_rows() {
126135
*/
127136
foreach ( $this->items as $item ) {
128137
?>
129-
<div class="plugin-card cloud-search-card plugin-card-<?php echo esc_attr( $item->id ); ?>">
138+
<div class="plugin-card cloud-snippet-card plugin-card-<?php echo esc_attr( $item->id ); ?>">
130139
<?php
131140
cloud_lts_display_column_hidden_input( 'code', $item );
132141
cloud_lts_display_column_hidden_input( 'name', $item );
133142
?>
134143
<div class="plugin-card-top">
135-
<div class="name column-name">
144+
<div class="column-name">
136145
<h3>
137146
<?php
147+
148+
// Grab first tag in array of tags.
149+
$category = count( $item->tags ) > 0 ? strtolower( esc_attr( $item->tags[0] ) ) : 'general';
150+
151+
printf(
152+
'<img src="%s" class="title-icon" alt="%s">',
153+
esc_url( "https://codesnippets.cloud/images/plugin-icons/$category-logo.png" ),
154+
esc_attr( $category )
155+
);
156+
138157
$link = code_snippets()->cloud_api->get_link_for_cloud_snippet( $item );
139158

140159
if ( $link ) {
@@ -151,25 +170,16 @@ public function display_rows() {
151170

152171
echo esc_html( $item->name );
153172

154-
// Grab first tag in array of tags.
155-
$category = count( $item->tags ) > 0 ? strtolower( esc_attr( $item->tags[0] ) ) : 'general';
156173

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-
);
162174

163175
echo '</a>';
164176
?>
165177
</h3>
166-
</div>
167-
<div class="action-links">
168-
<ul class="plugin-action-buttons">
169-
<?php cloud_lts_render_action_buttons( $item, 'search' ); ?>
178+
<ul class="action-buttons">
179+
<?php echo cloud_lts_build_action_links( $item, 'search' ); ?>
170180
</ul>
171181
</div>
172-
<div class="desc column-description">
182+
<div class="column-description">
173183
<p><?php echo wp_kses_post( $this->process_description( $item->description ) ); ?></p>
174184
<p class="authors">
175185
<cite>
@@ -186,64 +196,42 @@ public function display_rows() {
186196
</div>
187197
</div>
188198
<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 ) );
199+
<div class="cloud-meta-row">
200+
<div class="column-downloaded">
201+
<div class="badge <?php echo esc_attr( $this->cloud_api->get_status_badge( $item->status ) ); ?>-badge tooltip tooltip-block tooltip-end">
202+
<?php
200203

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+
echo esc_html( $this->cloud_api->get_status_label( $item->status ) );
204205

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
206+
if ( isset( $status_descriptions[ $item->status ] ) ) {
207+
echo '<span class="dashicons dashicons-info-outline"></span>';
208+
printf( '<div class="tooltip-content">%s</div>', esc_html( $status_descriptions[ $item->status ] ) );
209+
}
210+
?>
211+
</div>
212+
</div>
220213

221-
echo esc_html( $this->cloud_api->get_status_label( $item->status ) );
214+
<div class="column-votes">
215+
<svg xmlns="http://www.w3.org/2000/svg"
216+
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="thumbs-up">
217+
<path stroke-linecap="round" stroke-linejoin="round"
218+
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>
219+
</svg>
220+
<span class="num-votes" aria-hidden="true">
221+
<?php
222+
echo esc_html( number_format_i18n( $item->vote_count ) );
223+
?>
224+
</span>
225+
</div>
222226

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+
<div class="column-updated">
228+
<strong><?php esc_html_e( 'Last Updated:', 'code-snippets' ); ?></strong>
229+
<?php
230+
// translators: %s: Human-readable time difference.
231+
echo esc_html( sprintf( __( '%s ago', 'code-snippets' ), human_time_diff( strtotime( $item->updated ) ) ) );
227232
?>
228233
</div>
229234
</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>
247235
</div>
248236
</div>
249237
<?php
@@ -282,16 +270,17 @@ public function no_items() {
282270
*
283271
* @return Cloud_Snippets
284272
*/
285-
public function fetch_snippets(): Cloud_Snippets {
273+
public function fetch_snippets( int $per_page = 10, int $page_index = 0 ): Cloud_Snippets {
286274
// Check if search term has been entered.
287275
if ( isset( $_REQUEST['type'], $_REQUEST['cloud_search'], $_REQUEST['cloud_select'] ) &&
288-
'cloud_search' === sanitize_key( wp_unslash( $_REQUEST['type'] ) )
276+
'cloud_search' === sanitize_key( wp_unslash( $_REQUEST['type'] ) )
289277
) {
290278
// If we have a search query, then send a search request to cloud server API search endpoint.
291279
$search_query = sanitize_text_field( wp_unslash( $_REQUEST['cloud_search'] ) );
292280
$search_by = sanitize_text_field( wp_unslash( $_REQUEST['cloud_select'] ) );
293281

294-
return Cloud_API::fetch_search_results( $search_by, $search_query, $this->get_pagenum() - 1 );
282+
// Pass the provided 0-based page index to the API.
283+
return Cloud_API::fetch_search_results( $search_by, $search_query, $page_index );
295284
}
296285

297286
// If no search results, then return empty object.
@@ -331,23 +320,25 @@ public function display() {
331320
* @return void
332321
*/
333322
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();
323+
if ( empty( $this->_pagination_args ) ) {
324+
return;
325+
}
337326

338-
if ( 'top' === $which && $total_pages > 1 ) {
327+
$total_items = $this->_pagination_args['total_items'] ?? 0;
328+
$total_pages = $this->_pagination_args['total_pages'] ?? 0;
329+
// get_pagenum already returns a 1-based page number used for display.
330+
$pagenum_display = $this->get_pagenum();
331+
332+
if ( 'top' === $which && $total_pages >= 1 ) {
339333
$this->screen->render_screen_reader_content( 'heading_pagination' );
340334
}
341335

342-
$paginate = cloud_lts_pagination( $which, 'search', $total_items, $total_pages, $pagenum );
336+
$paginate = cloud_lts_pagination( $which, 'search', $total_items, $total_pages, $pagenum_display );
343337
$page_class = $paginate['page_class'];
344338
$output = $paginate['output'];
345339

346340
$this->_pagination = "<div class='tablenav-pages$page_class'>$output</div>";
347341

348-
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
349342
echo $this->_pagination;
350-
351-
// echo wp_kses_post( $this->_pagination ); TODO: This removes the top input box for page number.
352343
}
353344
}

src/php/cloud/list-table-shared-ops.php

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ function cloud_lts_display_column_hidden_input( string $column_name, Cloud_Snipp
2727
);
2828
}
2929

30+
/**
31+
* Display a hidden input field for a certain column and snippet value.
32+
*
33+
* @param string $column_name Column name.
34+
* @param Cloud_Snippet $snippet Column item.
35+
*
36+
* @return string HTML
37+
*/
38+
function cloud_lts_build_column_hidden_input( string $column_name, Cloud_Snippet $snippet ): string {
39+
return sprintf(
40+
'<input id="cloud-snippet-%s-%s" class="cloud-snippet-item" type="hidden" name="%s" value="%s" />',
41+
esc_attr( $column_name ),
42+
esc_attr( $snippet->id ),
43+
esc_attr( $column_name ),
44+
esc_attr( $snippet->$column_name )
45+
);
46+
}
47+
3048
/**
3149
* Process the download snippet action
3250
*
@@ -57,9 +75,9 @@ function cloud_lts_process_download_action( string $action, string $source, stri
5775
* @param Cloud_Snippet $cloud_snippet Snippet/Column item.
5876
* @param string $source Source - 'search' or 'codevault'.
5977
*
60-
* @return void
78+
* @return string Action link HTML.
6179
*/
62-
function cloud_lts_render_action_buttons( Cloud_Snippet $cloud_snippet, string $source ) {
80+
function cloud_lts_build_action_links( Cloud_Snippet $cloud_snippet, string $source ): string {
6381
$lang = Cloud_API::get_type_from_scope( $cloud_snippet->scope );
6482
$link = code_snippets()->cloud_api->get_link_for_cloud_snippet( $cloud_snippet );
6583
$is_licensed = code_snippets()->licensing->is_licensed();
@@ -74,54 +92,59 @@ function cloud_lts_render_action_buttons( Cloud_Snippet $cloud_snippet, string $
7492
'source' => $source,
7593
]
7694
);
77-
printf(
95+
return sprintf(
7896
'<li><a class="button button-primary" href="%s">%s</a></li>',
7997
esc_url( $update_url ),
8098
esc_html__( 'Update Available', 'code-snippets' )
8199
);
82100
} else {
83-
printf(
101+
return sprintf(
84102
'<li><a class="button" href="%s">%s</a></li>',
85103
esc_url( code_snippets()->get_snippet_edit_url( $link->local_id ) ),
86104
esc_html__( 'View', 'code-snippets' )
87105
);
88106
}
89-
90-
return;
91107
}
92108

93109
if ( $download ) {
94-
$download_url = add_query_arg(
95-
[
110+
$download_query = [
96111
'action' => 'download',
97112
'snippet' => $cloud_snippet->id,
98113
'source' => $source,
99-
]
100-
);
114+
];
101115

102-
printf(
116+
// Preserve current cloud page if present so downstream handlers receive pagination context.
117+
if ( isset( $_REQUEST['cloud_page'] ) ) {
118+
$download_query['cloud_page'] = (int) wp_unslash( $_REQUEST['cloud_page'] );
119+
}
120+
121+
$download_url = add_query_arg( $download_query );
122+
123+
$download_button = sprintf(
103124
'<li><a class="button button-primary" href="%s">%s</a></li>',
104125
esc_url( $download_url ),
105126
esc_html__( 'Download', 'code-snippets' )
106127
);
107128
} else {
108-
printf(
129+
$download_button = sprintf(
109130
'<li><span class="%s">%s <span class="tooltip-content">%s</span></span></li>',
110131
'button button-primary button-disabled tooltip tooltip-block tooltip-end',
111132
esc_html__( 'Download', 'code-snippets' ),
112133
esc_html__( 'This snippet type is only available in Code Snippets Pro', 'code-snippets' )
113134
);
114135
}
115136

116-
printf(
137+
$preview_button = sprintf(
117138
'<li><a href="%s" aria-label="%s" class="%s" data-snippet="%s" data-lang="%s">%s</a></li>',
118139
'#TB_inline?&width=700&height=500&inlineId=show-code-preview',
119140
esc_attr( $cloud_snippet->name ),
120-
'cloud-snippet-preview thickbox',
141+
'cloud-snippet-preview thickbox button',
121142
esc_attr( $cloud_snippet->id ),
122143
esc_attr( $lang ),
123144
esc_html__( 'Preview', 'code-snippets' )
124145
);
146+
147+
return $download_button . $preview_button;
125148
}
126149

127150
/**
@@ -140,7 +163,8 @@ function cloud_lts_pagination( string $which, string $source, int $total_items,
140163
$num = sprintf( _n( '%s item', '%s items', $total_items, 'code-snippets' ), number_format_i18n( $total_items ) );
141164
$output = '<span class="displaying-num">' . $num . '</span>';
142165

143-
$current = isset( $_REQUEST['cloud_page'] ) ? (int) $_REQUEST['cloud_page'] : $pagenum;
166+
$param_key = $source . '_page';
167+
$current = isset( $_REQUEST[ $param_key ] ) ? (int) $_REQUEST[ $param_key ] : $pagenum;
144168
$current_url = remove_query_arg( wp_removable_query_args() ) . '#' . $source;
145169

146170
$page_links = array();
@@ -234,8 +258,10 @@ function cloud_lts_pagination( string $which, string $source, int $total_items,
234258

235259
$output .= "\n<span class='$pagination_links_class'>" . implode( "\n", $page_links ) . '</span>';
236260

261+
$page_class = $total_pages ? '' : ' no-pages';
262+
237263
return [
238264
'output' => $output,
239-
'page_class' => $total_pages ? ( $total_pages < 2 ? ' one-page' : '' ) : ' no-pages',
265+
'page_class' => $page_class,
240266
];
241267
}

0 commit comments

Comments
 (0)