PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.6
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.6
4.5.6 4.5.5 4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 All 148 releases
← All changes | includes/setup-wizard/ultimate-post-kit-others-plugin.php +53 -75 4.5.0 → 4.5.6 View file →
@@ -93,42 +93,8 @@
93 93 }
94 94 }
95 95 }
96 96
97 - // Helper function for fallback URLs
98 - if (!function_exists('get_plugin_fallback_urls_usk')) {
99 - // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
100 - function get_plugin_fallback_urls_usk($plugin_slug) {
101 - // Handle different plugin slug formats
102 - if (strpos($plugin_slug, '/') !== false) {
103 - // If it's a file path like 'plugin-name/plugin-name.php', extract directory
104 - $plugin_slug_clean = dirname($plugin_slug);
105 - } else {
106 - // If it's just the plugin directory name, use it directly
107 - $plugin_slug_clean = $plugin_slug;
108 - }
109 -
110 - // Custom icon URLs for specific plugins that might not be on WordPress.org
111 - $custom_icons = [
112 - 'ar-viewer' => [
113 - 'https://ps.w.org/ar-viewer/assets/icon-256x256.gif',
114 - 'https://ps.w.org/ar-viewer/assets/icon-128x128.gif',
115 - ],
116 - ];
117 -
118 - // Return custom icons if available, otherwise use default WordPress.org URLs
119 - if (isset($custom_icons[$plugin_slug_clean])) {
120 - return $custom_icons[$plugin_slug_clean];
121 - }
122 -
123 - return [
124 - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-256x256.png", // Then PNG
125 - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-128x128.png", // Medium PNG
126 - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-256x256.gif", // Try GIF first
127 - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-128x128.gif", // Medium GIF
128 - ];
129 - }
130 - }
131 97 ?>
132 98
133 99 <div class="upk-dashboard-panel"
134 100 bdt-scrollspy="target: > div > div > .bdt-card; cls: bdt-animation-slide-bottom-small; delay: 300">
@@ -265,9 +231,9 @@
265 231 function renderPlugins(plugins) {
266 232 var html = '';
267 233
268 234 if (plugins.length === 0) {
269 - html = '<div class="bdt-text-center bdt-padding-large"><p><?php esc_html_e('No plugins available.', 'ultimate-post-kit'); ?></p></div>';
235 + html = '<div class="bdt-text-center bdt-padding-large"><p><?php echo esc_js(__('No plugins available.', 'ultimate-post-kit')); ?></p></div>';
270 236 } else {
271 237 plugins.forEach(function(plugin) {
272 238 // Skip own plugin (Ultimate Post Kit) when printing only; data still includes it for other plugins
273 239 if (plugin.slug === 'ultimate-post-kit') return;
@@ -275,21 +241,22 @@
275 241 var logoUrl = plugin.logo || '';
276 242 var pluginName = plugin.name || '';
277 243 var pluginSlug = plugin.slug || '';
278 244
279 - // Generate fallback logo URL if needed
280 - if (!logoUrl) {
281 - var actualSlug = pluginSlug.replace('.php', '').split('/')[0];
282 - logoUrl = 'https://ps.w.org/' + actualSlug + '/assets/icon-256x256.png';
283 - }
284 -
245 + // The logo URL comes from the WordPress.org API response. When it is
246 + // missing we show the local placeholder rather than guessing a remote
247 + // asset URL.
248 + var logoMarkup = upkSafeUrl(logoUrl)
249 + ? '<img src="' + upkEsc(upkSafeUrl(logoUrl)) + '" alt="' + upkEsc(pluginName) + '" class="bdt-plugin-logo" ' +
250 + 'onerror="this.style.display=\'none\'; this.nextElementSibling.style.display=\'flex\';">' +
251 + '<div class="default-plugin-icon" style="display:none;">📦</div>'
252 + : '<div class="default-plugin-icon" style="display:flex;">📦</div>';
253 +
285 254 html += '<div class="bdt-card bdt-card-body bdt-flex bdt-flex-middle bdt-flex-between">' +
286 255 '<div class="bdt-others-plugin-content">' +
287 256 '<div class="bdt-plugin-logo-wrap bdt-flex bdt-flex-middle">' +
288 257 '<div class="bdt-plugin-logo-container">' +
289 - '<img src="' + upkEsc(upkSafeUrl(logoUrl)) + '" alt="' + upkEsc(pluginName) + '" class="bdt-plugin-logo" ' +
290 - 'onerror="this.style.display=\'none\'; this.nextElementSibling.style.display=\'flex\';">' +
291 - '<div class="default-plugin-icon" style="display:none;">📦</div>' +
258 + logoMarkup +
292 259 '</div>' +
293 260 '<div class="bdt-others-plugin-user-wrap bdt-flex bdt-flex-middle">' +
294 261 '<h1 class="upk-feature-title">' + upkEsc(pluginName) + '</h1>' +
295 262 '</div>' +
@@ -294,20 +261,17 @@
294 261 '<h1 class="upk-feature-title">' + upkEsc(pluginName) + '</h1>' +
295 262 '</div>' +
296 263 '</div>' +
297 264 '<div class="bdt-others-plugin-content-text bdt-margin-top">';
298 -
299 - if (plugin.description) {
300 - html += '<p>' + upkEsc(plugin.description) + '</p>';
301 - }
302 -
265 +
303 266 // Active installs
267 + var installsCount = Number(plugin.active_installs_count) || 0;
304 268 html += '<span class="active-installs bdt-margin-small-top">' +
305 - '<?php esc_html_e("Active Installs: ", "ultimate-post-kit"); ?> ';
306 - if (plugin.active_installs_count > 0) {
307 - html += '<span class="installs-count">' + plugin.active_installs_count.toLocaleString() + '+</span>';
269 + '<?php echo esc_js(__('Active Installs: ', 'ultimate-post-kit')); ?> ';
270 + if (installsCount > 0) {
271 + html += '<span class="installs-count">' + upkEsc(installsCount.toLocaleString()) + '+</span>';
308 272 } else {
309 - html += '<span class="installs-count">Fewer than 10</span>';
273 + html += '<span class="installs-count"><?php echo esc_js(__('Fewer than 10', 'ultimate-post-kit')); ?></span>';
310 274 }
311 275 html += '</span>';
312 276
313 277 // Rating
@@ -313,29 +277,32 @@
313 277 // Rating
314 278 html += '<div class="bdt-others-plugin-rating bdt-margin-small-top bdt-flex bdt-flex-middle">' +
315 279 '<span class="bdt-others-plugin-rating-stars">';
316 280
317 - var rating = parseFloat(plugin.rating) || 0;
281 + // Clamp to 0-5 so malformed data cannot emit a runaway number of stars.
282 + var rating = Math.min(5, Math.max(0, parseFloat(plugin.rating) || 0));
318 283 var fullStars = Math.floor(rating);
319 284 var hasHalfStar = (rating - fullStars) >= 0.5;
320 285 var emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
321 -
322 - for (var i = 0; i < fullStars; i++) {
286 + var i;
287 +
288 + for (i = 0; i < fullStars; i++) {
323 289 html += '<i class="dashicons dashicons-star-filled"></i>';
324 290 }
325 291 if (hasHalfStar) {
326 292 html += '<i class="dashicons dashicons-star-half"></i>';
327 293 }
328 - for (var i = 0; i < emptyStars; i++) {
294 + for (i = 0; i < emptyStars; i++) {
329 295 html += '<i class="dashicons dashicons-star-empty"></i>';
330 296 }
331 297
332 298 html += '</span>' +
333 299 '<span class="bdt-others-plugin-rating-text bdt-margin-small-left">' +
334 - rating + ' <?php esc_html_e("out of 5 stars.", "ultimate-post-kit"); ?>';
300 + rating + ' <?php echo esc_js(__('out of 5 stars.', 'ultimate-post-kit')); ?>';
335 301
336 - if (plugin.num_ratings > 0) {
337 - html += '<span class="rating-count">(' + plugin.num_ratings.toLocaleString() + ' <?php esc_html_e("ratings", "ultimate-post-kit"); ?>)</span>';
302 + var numRatings = Number(plugin.num_ratings) || 0;
303 + if (numRatings > 0) {
304 + html += '<span class="rating-count">(' + upkEsc(numRatings.toLocaleString()) + ' <?php echo esc_js(__('ratings', 'ultimate-post-kit')); ?>)</span>';
338 305 }
339 306
340 307 html += '</span></div>';
341 308
@@ -341,9 +308,9 @@
341 308
342 309 // Downloads
343 310 if (plugin.downloaded_formatted) {
344 311 html += '<div class="bdt-others-plugin-downloads bdt-margin-small-top">' +
345 - '<span><?php esc_html_e("Downloads: ", "ultimate-post-kit"); ?>' + plugin.downloaded_formatted + '</span>' +
312 + '<span><?php echo esc_js(__('Downloads: ', 'ultimate-post-kit')); ?>' + upkEsc(plugin.downloaded_formatted) + '</span>' +
346 313 '</div>';
347 314 }
348 315
349 316 // Last updated
@@ -348,9 +315,9 @@
348 315
349 316 // Last updated
350 317 if (plugin.last_updated_formatted) {
351 318 html += '<div class="bdt-others-plugin-updated bdt-margin-small-top">' +
352 - '<span><?php esc_html_e("Last Updated: ", "ultimate-post-kit"); ?>' + plugin.last_updated_formatted + '</span>' +
319 + '<span><?php echo esc_js(__('Last Updated: ', 'ultimate-post-kit')); ?>' + upkEsc(plugin.last_updated_formatted) + '</span>' +
353 320 '</div>';
354 321 }
355 322
356 323 html += '</div></div>' +
@@ -359,24 +326,28 @@
359 326 // Show different buttons based on plugin status
360 327 if (plugin.status === 'active') {
361 328 html += '<span class="bdt-button bdt-button-success bdt-disabled">' +
362 329 '<span class="dashicons dashicons-yes"></span> ' +
363 - '<?php esc_html_e("Active", "ultimate-post-kit"); ?>' +
330 + '<?php echo esc_js(__('Active', 'ultimate-post-kit')); ?>' +
364 331 '</span>';
365 332 } else if (plugin.status === 'installed') {
366 - var activateUrl = '<?php echo esc_url( admin_url("plugins.php?action=activate&plugin=") ); ?>' + plugin.plugin_file + '&_wpnonce=' + plugin.activate_nonce;
367 - html += '<a class="bdt-button bdt-welcome-button" href="' + activateUrl + '">' +
368 - '<?php esc_html_e("Activate", "ultimate-post-kit"); ?>' +
333 + // URL-encode the query values: plugin_file contains slashes and
334 + // both parts land inside an href attribute.
335 + var activateUrl = <?php echo wp_json_encode( esc_url_raw( admin_url( 'plugins.php?action=activate&plugin=' ) ) ); ?> +
336 + encodeURIComponent(plugin.plugin_file || '') +
337 + '&_wpnonce=' + encodeURIComponent(plugin.activate_nonce || '');
338 + html += '<a class="bdt-button bdt-welcome-button" href="' + upkEsc(activateUrl) + '">' +
339 + '<?php echo esc_js(__('Activate', 'ultimate-post-kit')); ?>' +
369 340 '</a>';
370 341 } else {
371 - html += '<button class="bdt-button bdt-welcome-button upk-install-plugin" data-plugin-slug="' + upkEsc(pluginSlug) + '" data-nonce="<?php echo esc_attr( wp_create_nonce('upk_install_plugin_nonce') ); ?>">' +
372 - '<?php esc_html_e("Install", "ultimate-post-kit"); ?>' +
342 + html += '<button type="button" class="bdt-button bdt-welcome-button upk-install-plugin" data-plugin-slug="' + upkEsc(pluginSlug) + '" data-nonce="<?php echo esc_attr( wp_create_nonce('upk_install_plugin_nonce') ); ?>">' +
343 + '<?php echo esc_js(__('Install', 'ultimate-post-kit')); ?>' +
373 344 '</button>';
374 345 }
375 346
376 347 if (plugin.homepage && upkSafeUrl(plugin.homepage)) {
377 348 html += '<a class="bdt-button bdt-dashboard-sec-btn" target="_blank" rel="noopener noreferrer" href="' + upkEsc(upkSafeUrl(plugin.homepage)) + '">' +
378 - '<?php esc_html_e("Learn More", "ultimate-post-kit"); ?>' +
349 + '<?php echo esc_js(__('Learn More', 'ultimate-post-kit')); ?>' +
379 350 '</a>';
380 351 }
381 352
382 353 html += '</div></div>';
@@ -384,10 +355,13 @@
384 355 }
385 356
386 357 $list.html(html);
387 358
388 - // Handle plugin action buttons
389 - $('.upk-install-plugin').on('click', function(e) {
359 + // Handle plugin action buttons. Delegated from the list and
360 + // namespaced+unbound first: renderPlugins() runs again on every
361 + // retry, and a plain global bind stacked one handler per render,
362 + // firing duplicate install requests for a single click.
363 + $list.off('click.upkInstall').on('click.upkInstall', '.upk-install-plugin', function(e) {
390 364 e.preventDefault();
391 365
392 366 var $button = $(this);
393 367 var pluginSlug = $button.data('plugin-slug');
@@ -475,12 +449,15 @@
475 449 '<div class="upk-loading-dot"></div>' +
476 450 '<div class="upk-loading-dot"></div>' +
477 451 '</div>' +
478 452 '</div>' +
479 - '<p class="bdt-margin-small-top bdt-text-muted"><?php esc_html_e("Loading plugin data...", "ultimate-post-kit"); ?></p>' +
453 + '<p class="bdt-margin-small-top bdt-text-muted"><?php echo esc_js(__('Loading plugin data...', 'ultimate-post-kit')); ?></p>' +
480 454 '</div>'
481 455 );
482 - $list.show();
456 + // Set the display explicitly: the list is a CSS grid, and jQuery's
457 + // .show() can resolve the inline display:none to "block", which would
458 + // flatten the card grid into a single stacked column.
459 + $list.css('display', 'grid');
483 460 }
484 461
485 462 // Function to show error
486 463 function showError() {
@@ -503,11 +480,12 @@
503 480 /**
504 481 * AJAX handler for getting plugins data
505 482 */
506 483 public function ajax_get_plugins() {
507 - // Verify nonce
484 + // Verify nonce. Respond with JSON -- the caller parses the response as
485 + // JSON, so wp_die() here would surface as a generic "unable to load".
508 486 if (!check_ajax_referer('upk_get_plugins_nonce', 'nonce', false)) {
509 - wp_die(esc_html__('Security check failed.', 'ultimate-post-kit'));
487 + wp_send_json_error(['message' => __('Security check failed.', 'ultimate-post-kit')], 403);
510 488 }
511 489
512 490 // This data is only ever used on the plugin-install screen; gate it to
513 491 // users who could act on it rather than exposing it to any visitor.