PluginProbe
Ultimate Store Kit / 3.1.2
Ultimate Store Kit v3.1.2
3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 92 releases
ultimate-store-kit / includes / Admin / Feeds.php

Feeds.php in Ultimate Store Kit 3.1.2, at includes/Admin/Feeds.php

229 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace UltimateStoreKit\Admin;
4
5 use UltimateStoreKit\Base\Singleton;
6
7 if (! defined('ABSPATH')) {
8 exit;
9 }
10
11 /**
12 * Class Feeds
13 */
14 class Feeds {
15 use Singleton;
16 private $settings;
17
18 /**
19 * Max seconds to wait for a remote feed. Kept short so a slow or dead
20 * endpoint can never stall the admin dashboard into a gateway timeout.
21 */
22 const REQUEST_TIMEOUT = 5;
23
24 /**
25 * How long to skip remote requests after a failure.
26 */
27 const FAILURE_BACKOFF = HOUR_IN_SECONDS;
28
29 /**
30 * Static variable to track if the feed has been displayed
31 */
32 private static $feed_displayed = false;
33
34 /**
35 * Feeds constructor.
36 */
37 public function __construct($settings = null) {
38 $this->settings = $settings ?: [
39 'feed_title' => 'BdThemes News & Updates',
40 'transient_key' => 'bdthemes_product_feeds',
41 'feed_link' => 'https://bdthemes.com/feed',
42 'text_domain' => 'ultimate-store-kit',
43 'footer_links' => [
44 [
45 'url' => 'https://bdthemes.com/blog/',
46 'title' => 'Blog',
47 ],
48 [
49 'url' => 'https://bdthemes.com/knowledge-base/',
50 'title' => 'Docs',
51 ],
52 [
53 'url' => 'https://store.bdthemes.com/',
54 'title' => 'Get Pro',
55 ],
56 [
57 'url' => 'https://feedback.elementpack.pro/announcements/',
58 'title' => 'Changelog',
59 ],
60 ],
61 ];
62 add_action('wp_dashboard_setup', [$this, 'register_rss_feeds']);
63 }
64
65 /**
66 * Register RSS Feeds for Element Pack
67 */
68 public function register_rss_feeds() {
69 if (self::$feed_displayed) {
70 /**
71 * If the feed has already been displayed, do not add it again
72 */
73 return;
74 }
75
76 wp_add_dashboard_widget(
77 'bdt-dashboard-overview',
78 esc_html($this->settings['feed_title']),
79 [$this, 'display_rss_feeds_content'],
80 null,
81 null,
82 'column4',
83 'core'
84 );
85
86 /**
87 * Mark the feed as displayed
88 */
89 self::$feed_displayed = true;
90 }
91
92 /**
93 * Display RSS Feeds Content
94 */
95 public function display_rss_feeds_content() {
96 echo wp_kses_post($this->get_rss_posts_data());
97 }
98
99
100 /**
101 * Get RSS Posts Data
102 *
103 * @return string
104 */
105 private function get_rss_posts_data() {
106 // Written out in full rather than concatenated so the prefix is visible to
107 // static analysis. The resolved key is byte-identical to the previous
108 // $this->settings['transient_key'] . '_rss', so no cached data is orphaned.
109 $transient_key = 'bdthemes_product_feeds_rss';
110 $transient_failed_key = 'bdthemes_product_feeds_rss_failed';
111 $cached_data = get_transient($transient_key);
112
113 if (! empty($cached_data)) {
114 /**
115 * Decode as associative array
116 */
117 $rss_items = json_decode($cached_data, true);
118
119 if (! is_array($rss_items)) {
120 $rss_items = [];
121 }
122 } elseif (get_transient($transient_failed_key)) {
123 /**
124 * A recent fetch failed, so skip the blocking request entirely.
125 */
126 $rss_items = [];
127 } else {
128 include_once ABSPATH . WPINC . '/feed.php';
129
130 add_action('wp_feed_options', [$this, 'set_feed_timeout']);
131 $rss = fetch_feed($this->settings['feed_link']);
132 remove_action('wp_feed_options', [$this, 'set_feed_timeout']);
133
134 if (is_wp_error($rss)) {
135 set_transient($transient_failed_key, 1, self::FAILURE_BACKOFF);
136 return '<li>' . esc_html__('Items Not Found', 'ultimate-store-kit') . '.</li>';
137 }
138
139 $maxitems = $rss->get_item_quantity(5);
140 $rss_items = $rss->get_items(0, $maxitems);
141
142 /**
143 * Convert RSS items to a simpler array to avoid serialization issues
144 */
145 $simplified_rss_items = array_map(function ($item) {
146 return [
147 'title' => $item->get_title(),
148 'link' => $item->get_permalink(),
149 'date' => $item->get_date('U'),
150 'content' => $item->get_content(),
151 ];
152 }, $rss_items);
153
154 set_transient($transient_key, json_encode($simplified_rss_items), 6 * HOUR_IN_SECONDS);
155 $rss_items = $simplified_rss_items;
156 }
157
158 ob_start();
159 ?>
160 <div class="bdt-widget">
161 <ul>
162 <?php if (empty($rss_items)) : ?>
163 <li><?php esc_html_e('Items Not Found', 'ultimate-store-kit'); ?>.</li>
164 <?php else : ?>
165 <?php foreach ($rss_items as $item) : ?>
166 <li>
167 <a target="_blank" href="<?php echo esc_url($item['link']); ?>"
168 title="<?php echo esc_html($item['date']); ?>">
169 <?php if ($this->is_feed_item_new($item['date'])) : ?>
170 <span class="bdt-feed-badge bdt-feed-badge--new"><?php esc_html_e('New', 'ultimate-store-kit'); ?></span>
171 <?php endif; ?>
172 <?php echo esc_html($item['title']); ?>
173 </a>
174 <span class="bdt-date" style="display: block; margin: 0;">
175 <?php echo esc_html(human_time_diff($item['date'], current_time('timestamp')) . ' ' . __('ago', 'ultimate-store-kit')); ?>
176 </span>
177 <div class="bdt-summary">
178 <?php echo esc_html(wp_html_excerpt($item['content'], 120) . ' [...]'); ?>
179 </div>
180 </li>
181 <?php endforeach; ?>
182 <?php endif; ?>
183 </ul>
184 </div>
185 <p class="community-events-footer" style="margin: 12px -12px 6px -12px; padding: 12px 12px 0px;">
186 <?php
187 foreach ($this->settings['footer_links'] as $link) {
188 printf(
189 '<a href="%s" target="_blank">%s <span class="screen-reader-text"> (opens in a new tab)</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>',
190 esc_url($link['url']),
191 esc_html($link['title'])
192 );
193
194 if (next($this->settings['footer_links'])) {
195 echo ' | ';
196 }
197 }
198 ?>
199 </p>
200 <?php
201 return ob_get_clean();
202 }
203
204 /**
205 * Keep SimplePie's socket timeout in line with our own limit.
206 *
207 * @param object $feed SimplePie instance.
208 * @return void
209 */
210 public function set_feed_timeout($feed) {
211 $feed->set_timeout(self::REQUEST_TIMEOUT);
212 }
213
214 /**
215 * Check if a feed item is "new" (published within the last 7 days).
216 *
217 * @param int|string $date Unix timestamp.
218 * @return bool
219 */
220 private function is_feed_item_new($date) {
221 $timestamp = is_numeric($date) ? (int) $date : strtotime($date);
222 if (! $timestamp) {
223 return false;
224 }
225 $cutoff = time() - (7 * DAY_IN_SECONDS);
226 return $timestamp >= $cutoff;
227 }
228 }
229