Skip to content

Commit 8c21367

Browse files
feat: improved feeds validation UX (#1090)
1 parent e3949c4 commit 8c21367

14 files changed

+795
-148
lines changed

includes/admin/feedzy-rss-feeds-admin.php

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,4 +2513,133 @@ public function add_black_friday_data( $configs ) {
25132513

25142514
return $configs;
25152515
}
2516+
2517+
/**
2518+
* Validate the feed URL and check if it's a valid RSS/Atom feed.
2519+
*
2520+
* @return void
2521+
*/
2522+
public function validate_feed() {
2523+
try {
2524+
if (
2525+
! isset( $_POST['nonce'] ) ||
2526+
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), FEEDZY_BASEFILE )
2527+
) {
2528+
wp_send_json_error( array( 'message' => __( 'Security check failed.', 'feedzy-rss-feeds' ) ) );
2529+
}
2530+
2531+
$feed_urls = isset( $_POST['feed_url'] ) ? sanitize_text_field( wp_unslash( $_POST['feed_url'] ) ) : '';
2532+
2533+
if ( empty( $feed_urls ) ) {
2534+
wp_send_json_error( array( 'message' => __( 'Feed URL cannot be empty.', 'feedzy-rss-feeds' ) ) );
2535+
}
2536+
2537+
$urls = array_map( 'trim', explode( ',', $feed_urls ) );
2538+
$urls = array_filter( $urls );
2539+
2540+
if ( empty( $urls ) ) {
2541+
wp_send_json_error( array( 'message' => __( 'No valid URLs provided.', 'feedzy-rss-feeds' ) ) );
2542+
}
2543+
2544+
$results = array();
2545+
2546+
foreach ( $urls as $feed_url ) {
2547+
$feed_url = esc_url_raw( $feed_url );
2548+
2549+
if ( ! filter_var( $feed_url, FILTER_VALIDATE_URL ) ) {
2550+
$results[] = array(
2551+
'url' => $feed_url,
2552+
'status' => 'error',
2553+
'message' => __( 'Invalid URL format', 'feedzy-rss-feeds' ),
2554+
);
2555+
continue;
2556+
}
2557+
2558+
$feed = $this->fetch_feed( array( $feed_url ), '1_mins', array() );
2559+
2560+
if ( is_wp_error( $feed ) ) {
2561+
$results[] = array(
2562+
'url' => $feed_url,
2563+
'status' => 'error',
2564+
'message' => __( 'Error fetching feed: ', 'feedzy-rss-feeds' ) . $feed->get_error_message(),
2565+
);
2566+
continue;
2567+
}
2568+
2569+
if (
2570+
! is_object( $feed ) ||
2571+
! method_exists( $feed, 'get_item_quantity' )
2572+
) {
2573+
$results[] = array(
2574+
'url' => $feed_url,
2575+
'status' => 'error',
2576+
'message' => __( 'Invalid feed object returned', 'feedzy-rss-feeds' ),
2577+
);
2578+
continue;
2579+
}
2580+
2581+
try {
2582+
$items = $feed->get_item_quantity();
2583+
$title = $feed->get_title();
2584+
$error = $feed->error();
2585+
2586+
if ( is_array( $error ) && ! empty( $error ) ) {
2587+
$results[] = array(
2588+
'url' => $feed_url,
2589+
'status' => 'error',
2590+
'message' => __( 'Error fetching feed: ', 'feedzy-rss-feeds' ) . implode( ', ', $error ),
2591+
);
2592+
continue;
2593+
}
2594+
2595+
if ( 0 === $items ) {
2596+
$results[] = array(
2597+
'url' => $feed_url,
2598+
'status' => 'warning',
2599+
'message' => __( 'Feed is empty', 'feedzy-rss-feeds' ),
2600+
);
2601+
continue;
2602+
}
2603+
2604+
$results[] = array(
2605+
'url' => $feed_url,
2606+
'status' => 'success',
2607+
'message' => $title . sprintf(
2608+
/* translators: %d is the number of items found in the feed */
2609+
_n(
2610+
'%d item found',
2611+
'%d items found',
2612+
$items,
2613+
'feedzy-rss-feeds'
2614+
),
2615+
$items
2616+
),
2617+
'items' => $items,
2618+
'title' => $title,
2619+
);
2620+
2621+
} catch ( Throwable $e ) {
2622+
$results[] = array(
2623+
'url' => $feed_url,
2624+
'status' => 'error',
2625+
/* translators: %s is the error message */
2626+
'message' => sprintf( __( 'Error reading feed: %s', 'feedzy-rss-feeds' ), $e->getMessage() ),
2627+
);
2628+
}
2629+
}
2630+
2631+
wp_send_json_success(
2632+
array(
2633+
'results' => $results,
2634+
)
2635+
);
2636+
} catch ( Throwable $e ) {
2637+
wp_send_json_error(
2638+
array(
2639+
/* translators: %s is the error message */
2640+
'message' => sprintf( __( 'An error occurred: %s', 'feedzy-rss-feeds' ), $e->getMessage() ),
2641+
)
2642+
);
2643+
}
2644+
}
25162645
}

includes/admin/feedzy-rss-feeds-import.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ public function enqueue_styles() {
163163
array(
164164
'ajax' => array(
165165
'security' => wp_create_nonce( FEEDZY_BASEFILE ),
166+
'url' => admin_url( 'admin-ajax.php' ),
166167
),
167168
'i10n' => array(
168169
'importing' => __( 'Importing', 'feedzy-rss-feeds' ) . '...',
@@ -189,6 +190,10 @@ public function enqueue_styles() {
189190
esc_html__( 'Upload Import', 'feedzy-rss-feeds' )
190191
),
191192
'is_pro' => feedzy_is_pro(),
193+
'validation_messages' => array(
194+
'invalid_feed_url' => __( 'Invalid feed URL.', 'feedzy-rss-feeds' ),
195+
'error_validating_feed_url' => __( 'Error validating feed URL.', 'feedzy-rss-feeds' ),
196+
),
192197
),
193198
)
194199
);

includes/feedzy-rss-feeds.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ private function define_admin_hooks() {
202202
self::$instance->loader->add_filter( 'admin_footer', self::$instance->admin, 'handle_upgrade_submenu' );
203203
self::$instance->loader->add_action( 'current_screen', self::$instance->admin, 'handle_legacy' );
204204
self::$instance->loader->add_action( 'init', self::$instance->admin, 'register_settings' );
205+
self::$instance->loader->add_action( 'wp_ajax_feedzy_validate_feed', self::$instance->admin, 'validate_feed' );
205206

206207
// do not load this with the loader as this will need a corresponding remove_filter also.
207208
add_filter( 'update_post_metadata', array( self::$instance->admin, 'validate_category_feeds' ), 10, 5 );

includes/gutenberg/feedzy-rss-feeds-gutenberg-block.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public function feedzy_gutenberg_scripts() {
7575
'imagepath' => esc_url( FEEDZY_ABSURL . 'img/' ),
7676
'isPro' => feedzy_is_pro(),
7777
'upsellLinkBlockEditor' => esc_url( tsdk_translate_link( tsdk_utmify( FEEDZY_UPSELL_LINK, 'keywordsfilter', 'blockeditor' ) ) ),
78+
'nonce' => wp_create_nonce( FEEDZY_BASEFILE ),
79+
'url' => admin_url( 'admin-ajax.php' ),
7880
)
7981
);
8082

includes/gutenberg/feedzy-rss-feeds-loop-block.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public function register_block() {
7373
array(
7474
'imagepath' => esc_url( FEEDZY_ABSURL . 'img/' ),
7575
'defaultImage' => esc_url( FEEDZY_ABSURL . 'img/feedzy.svg' ),
76+
'url' => admin_url( 'admin-ajax.php' ),
77+
'nonce' => wp_create_nonce( FEEDZY_BASEFILE ),
7678
'isPro' => feedzy_is_pro(),
7779
)
7880
);

includes/views/css/import-metabox-edit.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,33 @@ span.feedzy-spinner {
202202
.fz-import-field.hidden {
203203
display: none;
204204
}
205+
206+
.feedzy-wrap .fz-validation-message {
207+
margin: 10px 0;
208+
padding: 12px 15px;
209+
border-radius: 4px;
210+
font-size: 14px;
211+
position: relative;
212+
z-index: 10;
213+
display: flex;
214+
flex-direction: column;
215+
align-items: flex-start;
216+
gap: 8px;
217+
border: 1px solid #5555559c;
218+
}
219+
220+
.feedzy-wrap .fz-validation-message button {
221+
margin-left: auto;
222+
}
223+
224+
.fz-validation-message .fz-success {
225+
color: #155724;
226+
}
227+
228+
.fz-validation-message .fz-error {
229+
color: #721c24;
230+
}
231+
232+
.fz-validation-message .fz-warning {
233+
color: #856404;
234+
}

includes/views/import-metabox-edit.php

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,29 @@
4242
<div class="fz-input-group-left">
4343
<div class="fz-group">
4444
<div class="fz-input-icon">
45-
<input type="text" id="feedzy-import-source" title="<?php esc_attr_e( 'Make sure you validate the feed by using the validate button on the right', 'feedzy-rss-feeds' ); ?>"
46-
placeholder="<?php esc_attr_e( 'Paste your feed URL and click the plus icon to add it in the list', 'feedzy-rss-feeds' ); ?>"
47-
class="form-control" />
48-
<div class="fz-input-group-append">
49-
<button class="fz-plus-btn add-outside-tags" disabled>
50-
<span class="dashicons dashicons-plus-alt2"></span>
51-
</button>
45+
<div class="fz-feed-validator-wrapper">
46+
<input type="text"
47+
id="feedzy-import-source"
48+
title="<?php esc_attr_e( 'Make sure you validate the feed by using the validate button on the right', 'feedzy-rss-feeds' ); ?>"
49+
placeholder="<?php esc_attr_e( 'Paste your feed URL and click the plus icon to add it in the list', 'feedzy-rss-feeds' ); ?>"
50+
class="form-control" />
51+
<div class="fz-input-group-append">
52+
<button class="btn btn-flate btn-icon add-outside-tags"
53+
type="button"
54+
id="feedzy-validate-feed"
55+
role="button"
56+
title="<?php esc_attr_e( 'Validate Feed', 'feedzy-rss-feeds' ); ?>">
57+
<i class="dashicons dashicons-plus-alt2" aria-hidden="true"></i>
58+
</button>
59+
</div>
5260
</div>
5361
</div>
54-
<div class="cta">
55-
<a class="btn btn-flate btn-icon" id="feedzy-validate-feed" target="_blank" data-href-base="https://validator.w3.org/feed/check.cgi?url="
56-
href="#" title="<?php esc_attr_e( 'Validate Feed', 'feedzy-rss-feeds' ); ?>"><i
57-
title="<?php esc_attr_e( 'Validate Feed', 'feedzy-rss-feeds' ); ?>"
58-
class="dashicons dashicons-external"></i></a>
59-
</div>
6062
</div>
6163
<div class="help-text">
6264
<?php esc_html_e( 'You can add multiple sources at once, by separating them with commas. Make sure to use the validate button. Invalid feeds may not import anything.', 'feedzy-rss-feeds' ); ?>
6365
</div>
66+
<div class="fz-validation-summary">
67+
</div>
6468
</div>
6569
<div class="fz-input-group-right">
6670
<div class="dropdown">

0 commit comments

Comments
 (0)