PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.2.0
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.2.0
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 4.1.3 4.1.4 All 146 releases
ultimate-post-kit / includes / setup-wizard / views / integration.php

integration.php in Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets 4.2.0, at includes/setup-wizard/views/integration.php

524 lines 24.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Integration Step
4 */
5
6 namespace UltimatePostKit\SetupWizard;
7
8 if (!defined('ABSPATH')) {
9 exit;
10 }
11
12 // Include the required classes
13 require_once __DIR__ . '/../class-plugin-integration-helper.php';
14 require_once __DIR__ . '/../class-remote-data-handler.php';
15
16 // Helper function for time formatting
17 if (!function_exists('format_last_updated_usk')) {
18 function format_last_updated_usk($date_string) {
19 if (empty($date_string)) {
20 return __('Unknown', 'ultimate-post-kit');
21 }
22
23 $date = strtotime($date_string);
24 if (!$date) {
25 return __('Unknown', 'ultimate-post-kit');
26 }
27
28 $diff = current_time('timestamp') - $date;
29
30 if ($diff < 60) {
31 return __('Just now', 'ultimate-post-kit');
32 } elseif ($diff < 3600) {
33 $minutes = floor($diff / 60);
34 /* translators: %d: number of minutes */
35 return sprintf(_n('%d minute ago', '%d minutes ago', $minutes, 'ultimate-post-kit'), $minutes);
36 } elseif ($diff < 86400) {
37 $hours = floor($diff / 3600);
38 /* translators: %d: number of hours */
39 return sprintf(_n('%d hour ago', '%d hours ago', $hours, 'ultimate-post-kit'), $hours);
40 } elseif ($diff < 2592000) { // 30 days
41 $days = floor($diff / 86400);
42 /* translators: %d: number of days */
43 return sprintf(_n('%d day ago', '%d days ago', $days, 'ultimate-post-kit'), $days);
44 } elseif ($diff < 31536000) { // 1 year
45 $months = floor($diff / 2592000);
46 /* translators: %d: number of months */
47 return sprintf(_n('%d month ago', '%d months ago', $months, 'ultimate-post-kit'), $months);
48 } else {
49 $years = floor($diff / 31536000);
50 /* translators: %d: number of years */
51 return sprintf(_n('%d year ago', '%d years ago', $years, 'ultimate-post-kit'), $years);
52 }
53 }
54 }
55
56 // Helper function for fallback URLs
57 if (!function_exists('get_plugin_fallback_urls_usk')) {
58 function get_plugin_fallback_urls_usk($plugin_slug) {
59 // Handle different plugin slug formats
60 if (strpos($plugin_slug, '/') !== false) {
61 // If it's a file path like 'plugin-name/plugin-name.php', extract directory
62 $plugin_slug_clean = dirname($plugin_slug);
63 } else {
64 // If it's just the plugin directory name, use it directly
65 $plugin_slug_clean = $plugin_slug;
66 }
67
68 // Custom icon URLs for specific plugins that might not be on WordPress.org
69 $custom_icons = [
70 'ar-viewer' => [
71 'https://ps.w.org/ar-viewer/assets/icon-256x256.gif',
72 'https://ps.w.org/ar-viewer/assets/icon-128x128.gif',
73 ],
74 ];
75
76 // Return custom icons if available, otherwise use default WordPress.org URLs
77 if (isset($custom_icons[$plugin_slug_clean])) {
78 return $custom_icons[$plugin_slug_clean];
79 }
80
81 return [
82 "https://ps.w.org/{$plugin_slug_clean}/assets/icon-256x256.png", // Large PNG
83 "https://ps.w.org/{$plugin_slug_clean}/assets/icon-128x128.png", // Medium PNG
84 ];
85 }
86 }
87
88 // Define plugin slugs
89 $plugin_slugs = array(
90 'bdthemes-element-pack-lite',
91 'bdthemes-prime-slider-lite/bdthemes-prime-slider.php',
92 'ultimate-post-kit',
93 'ultimate-store-kit',
94 'zoloblocks',
95 'pixel-gallery',
96 'live-copy-paste',
97 'spin-wheel',
98 'ai-image',
99 'dark-reader',
100 'ar-viewer',
101 'smart-admin-assistant',
102 'website-accessibility',
103 );
104
105 // Get enhanced plugin data using the remote data handler
106 $upk_plugins = Remote_Data_Handler::get_remote_plugins();
107
108 // Check if we have cached data
109 $has_cached_data = !empty($upk_plugins);
110
111 // If no cached data, don't fetch immediately - let JavaScript handle it
112 if (!$has_cached_data) {
113 $upk_plugins = []; // Empty array for initial load
114 }
115 ?>
116
117 <div class="bdt-wizard-step bdt-setup-wizard-integration" data-step="integration">
118 <h2><?php esc_html_e('Add More Firepower', 'ultimate-post-kit'); ?></h2>
119 <p><?php esc_html_e('You can onboard additional powerful plugins to extend your web design capabilities.', 'ultimate-post-kit'); ?></p>
120
121 <div class="progress-bar-container">
122 <div id="plugin-install-progress" class="progress-bar"></div>
123 </div>
124
125 <form method="POST" id="upk-install-plugins">
126 <!-- Loading state - shown during plugin installation -->
127 <div class="upk-loading-state" id="upk-install-loading" style="display: none; text-align: center; padding: 40px;">
128 <div class="upk-loading-dots">
129 <div class="upk-loading-dot"></div>
130 <div class="upk-loading-dot"></div>
131 <div class="upk-loading-dot"></div>
132 </div>
133 <p style="margin-top: 20px;" id="upk-loading-message"><?php esc_html_e('Installing plugins...', 'ultimate-post-kit'); ?></p>
134 </div>
135
136 <!-- Initial loading state - shown while fetching plugin data -->
137 <?php if (!$has_cached_data): ?>
138 <div class="upk-loading-state" id="upk-initial-loading" style="text-align: center; padding: 40px;">
139 <div class="upk-loading-dots">
140 <div class="upk-loading-dot"></div>
141 <div class="upk-loading-dot"></div>
142 <div class="upk-loading-dot"></div>
143 </div>
144 <p style="margin-top: 20px;"><?php esc_html_e('Loading plugin data...', 'ultimate-post-kit'); ?></p>
145 </div>
146 <?php endif; ?>
147
148 <div class="bdt-plugin-list" id="upk-integration-plugin-list">
149 <?php if ($has_cached_data): ?>
150 <?php
151 $predefined = \UltimatePostKit\SetupWizard\Plugin_Integration_Helper::get_predefined_plugins();
152 $recommended_by_slug = [];
153 foreach ($predefined as $key => $config) {
154 $dir = (strpos($key, '/') !== false) ? dirname($key) : $key;
155 $recommended_by_slug[ $dir ] = !empty($config['recommended']);
156 }
157 foreach ($upk_plugins as $slug_key => $plugin) :
158 // Skip own plugin (Ultimate Post Kit) when printing only; data still includes it for other plugins
159 if ($slug_key === 'ultimate-post-kit' ) continue;
160 // Use enhanced status if available, otherwise fall back to old method
161 $plugin_status = $plugin['status'] ?? 'unknown';
162 if ($plugin_status === 'unknown') {
163 // Fallback to old method for compatibility
164 $is_active = is_plugin_active($plugin['slug']);
165 } else {
166 // Use enhanced status
167 $is_active = ($plugin_status === 'active');
168 }
169 $plugin_recommended = !empty($recommended_by_slug[ $slug_key ]);
170 $is_recommended = $plugin_recommended && !$is_active;
171 ?>
172 <label class="plugin-item" data-slug="<?php echo esc_attr($plugin['slug']); ?>">
173 <span class="bdt-flex bdt-flex-middle bdt-flex-between bdt-margin-small-bottom">
174 <span class="bdt-plugin-logo">
175 <?php
176 $logo_url = $plugin['logo'] ?? '';
177 $plugin_name = $plugin['name'] ?? '';
178 $plugin_slug = $plugin['slug'] ?? '';
179
180 if (!empty($logo_url) && filter_var($logo_url, FILTER_VALIDATE_URL)) {
181 // Show the original logo from API
182 echo '<img src="' . esc_url($logo_url) . '" alt="' . esc_attr($plugin_name) . '" onerror="this.style.display=\'none\'; this.nextElementSibling.style.display=\'flex\';">';
183 echo '<div class="default-plugin-icon" style="display:none;">📦</div>';
184 } else {
185 // Generate fallback URLs for WordPress.org
186 $actual_slug = (strpos($plugin_slug, '/') !== false) ? dirname($plugin_slug) : $plugin_slug;
187 $fallback_urls = get_plugin_fallback_urls_usk($actual_slug);
188
189 echo '<img src="' . esc_url($fallback_urls[0]) . '" alt="' . esc_attr($plugin_name) . '" onerror="this.style.display=\'none\'; this.nextElementSibling.style.display=\'flex\';">';
190 echo '<div class="default-plugin-icon" style="display:none;">📦</div>';
191 }
192 ?>
193 </span>
194
195 <div class="bdt-plugin-badge-switch-wrap">
196
197 <?php if ($is_recommended) : ?>
198 <span class="recommended-badge"><?php esc_html_e('Recommended', 'ultimate-post-kit'); ?></span>
199 <?php endif; ?>
200
201 <?php if ($is_active) : ?>
202 <span class="active-badge"><?php esc_html_e('ACTIVE', 'ultimate-post-kit'); ?></span>
203 <?php endif; ?>
204 <?php
205 if (!$is_active) : ?>
206 <label class="switch">
207 <input type="checkbox" class="plugin-slider-checkbox" <?php echo $plugin_recommended ? 'checked' : ''; ?>
208 name="plugins[]<?php echo isset($plugin['slug']) ? wp_kses_post($plugin['slug']) : ''; ?>">
209 <span class="slider round"></span>
210 </label>
211 <?php
212 endif;
213 ?>
214 </div>
215 </span>
216 <div class="bdt-flex bdt-flex-middle">
217 <span class="bdt-plugin-name">
218 <?php echo wp_kses_post($plugin['name']); ?>
219 </span>
220 </div>
221
222 <span class="active-installs">
223 <?php esc_html_e('Active Installs: ', 'ultimate-post-kit');
224 if (isset($plugin['active_installs_count']) && $plugin['active_installs_count'] > 0) {
225 echo ' <span class="installs-count">' . number_format($plugin['active_installs_count']) . '+' . '</span>';
226 } else {
227 echo '<span class="installs-count">Fewer than 10</span>';
228 }
229 ?>
230 </span>
231
232 <?php if (isset($plugin['downloaded_formatted']) && !empty($plugin['downloaded_formatted'])): ?>
233 <span class="downloads"><?php esc_html_e('Downloads: ', 'ultimate-post-kit'); echo wp_kses_post($plugin['downloaded_formatted']); ?></span>
234 <?php endif; ?>
235
236 <div class="rating-section">
237 <div class="wporg-ratings" title="<?php echo esc_attr($plugin['rating'] ?? '0'); ?> out of 5 stars" style="color:var(--wp--preset--color--pomegrade-1, #e26f56);">
238 <?php
239 $rating = floatval($plugin['rating'] ?? 0);
240 $full_stars = floor($rating);
241 $has_half_star = ($rating - $full_stars) >= 0.5;
242 $empty_stars = 5 - $full_stars - ($has_half_star ? 1 : 0);
243
244 // Full stars
245 for ($i = 0; $i < $full_stars; $i++) {
246 echo '<span class="dashicons dashicons-star-filled"></span>';
247 }
248
249 // Half star
250 if ($has_half_star) {
251 echo '<span class="dashicons dashicons-star-half"></span>';
252 }
253
254 // Empty stars
255 for ($i = 0; $i < $empty_stars; $i++) {
256 echo '<span class="dashicons dashicons-star-empty"></span>';
257 }
258 ?>
259 </div>
260 <span class="rating-text">
261 <?php echo esc_html($plugin['rating'] ?? '0'); ?> out of 5 stars.
262 <?php if (isset($plugin['num_ratings']) && $plugin['num_ratings'] > 0): ?>
263 <span class="rating-count">(<?php echo number_format($plugin['num_ratings']); ?> ratings)</span>
264 <?php endif; ?>
265 </span>
266 </div>
267
268 <?php
269 // Use the enhanced last_updated_formatted if available, otherwise fall back to formatting
270 if (isset($plugin['last_updated_formatted']) && !empty($plugin['last_updated_formatted'])): ?>
271 <span class="last-updated"><?php esc_html_e('Last Updated: ', 'ultimate-post-kit'); echo esc_html($plugin['last_updated_formatted']); ?></span>
272 <?php elseif (isset($plugin['last_updated']) && !empty($plugin['last_updated'])): ?>
273 <span class="last-updated"><?php esc_html_e('Last Updated: ', 'ultimate-post-kit'); echo esc_html(format_last_updated_usk($plugin['last_updated'])); ?></span>
274 <?php endif; ?>
275
276 </label>
277 <?php
278 endforeach; ?>
279 <?php endif; ?>
280 </div>
281
282 <div class="wizard-navigation bdt-margin-top">
283 <button class="bdt-button bdt-button-primary d-none" type="submit" id="upk-install-plugins-btn">
284 <?php esc_html_e('Install and Continue', 'ultimate-post-kit'); ?>
285 </button>
286 <div class="bdt-close-button bdt-margin-left bdt-wizard-next" data-step="finish"><?php esc_html_e('Skip', 'ultimate-post-kit'); ?></div>
287 </div>
288 </form>
289
290 <div class="bdt-wizard-navigation">
291 <button class="bdt-button bdt-button-secondary bdt-wizard-prev" data-step="features">
292 <span><i class="dashicons dashicons-arrow-left-alt"></i></span>
293 <?php esc_html_e('Previous Step', 'ultimate-post-kit'); ?>
294 </button>
295 </div>
296 </div>
297
298 <style>
299 .upk-loading-dots {
300 display: flex;
301 justify-content: center;
302 gap: 8px;
303 margin: 20px 0;
304 }
305
306 .upk-loading-dot {
307 width: 12px;
308 height: 12px;
309 background-color: #0073aa;
310 border-radius: 50%;
311 animation: upk-wave 1.4s ease-in-out infinite both;
312 }
313
314 .upk-loading-dot:nth-child(1) {
315 animation-delay: -0.32s;
316 }
317
318 .upk-loading-dot:nth-child(2) {
319 animation-delay: -0.16s;
320 }
321
322 .upk-loading-dot:nth-child(3) {
323 animation-delay: 0s;
324 }
325
326 @keyframes upk-wave {
327 0%, 80%, 100% {
328 transform: scale(0.8);
329 opacity: 0.5;
330 }
331 40% {
332 transform: scale(1.2);
333 opacity: 1;
334 }
335 }
336 </style>
337
338 <script>
339 jQuery(document).ready(function($) {
340 let integrationDataLoaded = false;
341
342 // Function to load integration data
343 function loadIntegrationData() {
344 if (integrationDataLoaded) return;
345
346 const $pluginList = $('#upk-integration-plugin-list');
347 const $initialLoading = $('#upk-initial-loading');
348
349 // Don't add another loading state if initial loading is visible
350 // Just keep the existing one
351
352 // Make AJAX request to get plugin data
353 $.ajax({
354 url: ajaxurl,
355 type: 'POST',
356 data: {
357 action: 'upk_get_plugins',
358 nonce: '<?php echo esc_attr( wp_create_nonce('upk_get_plugins_nonce') ); ?>'
359 },
360 success: function(response) {
361 if (response.success && response.data.plugins) {
362 // Hide the initial loading div
363 $initialLoading.hide();
364 renderPluginList(response.data.plugins);
365 integrationDataLoaded = true;
366 } else {
367 showError('Unable to load plugin data.');
368 }
369 },
370 error: function() {
371 showError('Network error occurred while loading plugin data.');
372 }
373 });
374 }
375
376 // Function to render plugin list
377 function renderPluginList(plugins) {
378 const $pluginList = $('#upk-integration-plugin-list');
379 let html = '';
380
381 if (plugins.length === 0) {
382 html = '<div class="upk-no-plugins" style="text-align: center; padding: 40px;"><p>No plugins found.</p></div>';
383 } else {
384 plugins.forEach(function(plugin) {
385 // Skip own plugin (Ultimate Post Kit) when printing only; data still includes it for other plugins
386 if (plugin.slug === 'ultimate-post-kit') return;
387 const isActive = plugin.status === 'active';
388 const isRecommended = plugin.recommended && !isActive;
389
390 html += `
391 <label class="plugin-item" data-slug="${plugin.slug}">
392 <span class="bdt-flex bdt-flex-middle bdt-flex-between bdt-margin-small-bottom">
393 <span class="bdt-plugin-logo">
394 ${generatePluginLogo(plugin)}
395 </span>
396 <div class="bdt-plugin-badge-switch-wrap">
397 ${isRecommended ? '<span class="recommended-badge">Recommended</span>' : ''}
398 ${isActive ? '<span class="active-badge">ACTIVE</span>' : ''}
399 ${!isActive ? `
400 <label class="switch">
401 <input type="checkbox" class="plugin-slider-checkbox" ${plugin.recommended ? 'checked' : ''} name="plugins[]${plugin.slug}">
402 <span class="slider round"></span>
403 </label>
404 ` : ''}
405 </div>
406 </span>
407 <div class="bdt-flex bdt-flex-middle">
408 <span class="bdt-plugin-name">${plugin.name}</span>
409 </div>
410 <span class="active-installs">
411 Active Installs:
412 <span class="installs-count">${plugin.active_installs_count > 0 ? plugin.active_installs_count.toLocaleString() + '+' : 'Fewer than 10'}</span>
413 </span>
414 ${plugin.downloaded_formatted ? `<span class="downloads">Downloads: ${plugin.downloaded_formatted}</span>` : ''}
415 <div class="rating-section">
416 <div class="wporg-ratings" title="${plugin.rating} out of 5 stars" style="color:var(--wp--preset--color--pomegrade-1, #e26f56);">
417 ${generateStarRating(plugin.rating)}
418 </div>
419 <span class="rating-text">
420 ${plugin.rating} out of 5 stars.
421 ${plugin.num_ratings > 0 ? `<span class="rating-count">(${plugin.num_ratings.toLocaleString()} ratings)</span>` : ''}
422 </span>
423 </div>
424 ${plugin.last_updated_formatted ? `<span class="last-updated">Last Updated: ${plugin.last_updated_formatted}</span>` : ''}
425 </label>
426 `;
427 });
428 }
429
430 $pluginList.html(html);
431 }
432
433 // Helper function to generate plugin logo
434 function generatePluginLogo(plugin) {
435 if (plugin.logo && plugin.logo.match(/^https?:\/\//)) {
436 return `<img src="${plugin.logo}" alt="${plugin.name}" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
437 <div class="default-plugin-icon" style="display:none;">📦</div>`;
438 } else {
439 const slug = plugin.slug.includes('/') ? plugin.slug.split('/')[0] : plugin.slug;
440 return `<img src="https://ps.w.org/${slug}/assets/icon-256x256.png" alt="${plugin.name}" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
441 <div class="default-plugin-icon" style="display:none;">📦</div>`;
442 }
443 }
444
445 // Helper function to generate star rating
446 function generateStarRating(rating) {
447 const fullStars = Math.floor(rating);
448 const hasHalfStar = (rating - fullStars) >= 0.5;
449 const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
450
451 let html = '';
452 for (let i = 0; i < fullStars; i++) {
453 html += '<span class="dashicons dashicons-star-filled"></span>';
454 }
455 if (hasHalfStar) {
456 html += '<span class="dashicons dashicons-star-half"></span>';
457 }
458 for (let i = 0; i < emptyStars; i++) {
459 html += '<span class="dashicons dashicons-star-empty"></span>';
460 }
461 return html;
462 }
463
464 // Function to show error
465 function showError(message) {
466 const $pluginList = $('#upk-integration-plugin-list');
467 const $initialLoading = $('#upk-initial-loading');
468
469 // Hide initial loading
470 $initialLoading.hide();
471
472 // Show error in plugin list
473 $pluginList.html(`
474 <div class="upk-error-state" style="text-align: center; padding: 40px;">
475 <p style="color: #d63638;">${message}</p>
476 <button type="button" class="bdt-button bdt-button-secondary" onclick="location.reload()">Retry</button>
477 </div>
478 `);
479 }
480
481 // Detect when integration tab becomes active
482 function observeIntegrationTab() {
483 // Check if integration step is currently visible
484 const $integrationStep = $('.bdt-setup-wizard-integration');
485
486 if ($integrationStep.length) {
487 // Create a MutationObserver to detect when the integration step becomes visible
488 const observer = new MutationObserver(function(mutations) {
489 mutations.forEach(function(mutation) {
490 if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
491 const $target = $(mutation.target);
492 if ($target.hasClass('bdt-setup-wizard-integration') && $target.is(':visible')) {
493 loadIntegrationData();
494 observer.disconnect(); // Stop observing after first load
495 }
496 }
497 });
498 });
499
500 // Start observing
501 observer.observe($integrationStep[0], {
502 attributes: true,
503 attributeFilter: ['class']
504 });
505
506 // Also check immediately if it's already visible
507 if ($integrationStep.is(':visible') && !integrationDataLoaded) {
508 loadIntegrationData();
509 }
510 }
511 }
512
513 // Initialize tab observation
514 observeIntegrationTab();
515
516 // Fallback: Also try to detect tab clicks (for different wizard implementations)
517 $(document).on('click', '[data-step="integration"], .bdt-wizard-step[data-step="integration"]', function() {
518 if (!integrationDataLoaded) {
519 setTimeout(loadIntegrationData, 100);
520 }
521 });
522 });
523 </script>
524