PluginProbe
Listdom: AI-powered Business Directory with Classifieds Ads Listings / trunk
Listdom: AI-powered Business Directory with Classifieds Ads Listings vtrunk
6.0.0 5.9.0 5.8.1 5.8.0 5.7.0 5.6.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.2.0 1.2.1 1.3.0 1.3.1 1.4.0 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.8.0 1.9.0 All 75 releases
listdom / app / includes / feed.php

feed.php in Listdom: AI-powered Business Directory with Classifieds Ads Listings trunk, at app/includes/feed.php

319 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class LSD_Feed extends LSD_Base
4 {
5 private const DEFAULT_FEED_NAME = 'listdom-listings';
6
7 public function init()
8 {
9 add_action('init', [$this, 'register']);
10 }
11
12 public static function feed_name(): string
13 {
14 $settings = LSD_Options::settings();
15
16 $feed_name = $settings['rss_slug'] ?? self::DEFAULT_FEED_NAME;
17 $feed_name = sanitize_title($feed_name);
18 if ($feed_name === '') $feed_name = self::DEFAULT_FEED_NAME;
19
20 return apply_filters('lsd_listings_feed_name', $feed_name);
21 }
22
23 public static function feed_url(): string
24 {
25 return get_feed_link(self::feed_name());
26 }
27
28 public function register()
29 {
30 if (!$this->is_enabled()) return;
31
32 add_feed(self::feed_name(), [$this, 'render']);
33 add_action('pre_get_posts', [$this, 'pre_get_posts']);
34 add_action('wp_head', [$this, 'head_link']);
35 add_filter('the_content_feed', [$this, 'filter_content'], 10, 2);
36 add_filter('the_excerpt_rss', [$this, 'filter_excerpt']);
37 }
38
39 private function is_enabled(): bool
40 {
41 $settings = LSD_Options::settings();
42
43 return !isset($settings['rss_status']) || $settings['rss_status'];
44 }
45
46 public function render()
47 {
48 if (!$this->is_enabled())
49 {
50 status_header(404);
51 exit;
52 }
53
54 if (!$this->matches_preferred_path())
55 {
56 status_header(404);
57 exit;
58 }
59
60 do_feed_rss2(false);
61 }
62
63 public function pre_get_posts($query)
64 {
65 if (!$this->is_enabled()) return;
66 if (!($query instanceof WP_Query)) return;
67 if (is_admin() || !$query->is_main_query() || !$query->is_feed()) return;
68
69 $feed = $query->get('feed');
70 if ($feed !== self::feed_name()) return;
71
72 $query->set('post_type', LSD_Base::PTYPE_LISTING);
73 $query->set('post_status', 'publish');
74 $query->set('orderby', 'date');
75 $query->set('order', 'DESC');
76 $query->set('ignore_sticky_posts', 1);
77
78 $per_page = absint(get_option('posts_per_rss'));
79 $per_page = (int) apply_filters('lsd_listings_feed_posts_per_page', $per_page);
80 if ($per_page <= 0) $per_page = absint(get_option('posts_per_page'));
81
82 if ($per_page > 0)
83 {
84 $query->set('posts_per_rss', $per_page);
85 $query->set('posts_per_page', $per_page);
86 }
87 }
88
89 public function head_link()
90 {
91 if (!$this->is_enabled()) return;
92
93 $url = self::feed_url();
94 $title = sprintf(
95 /* translators: 1: Site title. */
96 esc_html__('%s Listings Feed', 'listdom'),
97 get_bloginfo('name')
98 );
99
100 $title = apply_filters('lsd_listings_feed_headline', $title, $url);
101
102 echo '<link rel="alternate" type="application/rss+xml" title="' . esc_attr($title) . '" href="' . esc_url($url) . '">' . "\n";
103 }
104
105 public function filter_content($content, $feed_type): string
106 {
107 if (!is_feed(self::feed_name())) return $content;
108
109 return $this->append_details($content, true);
110 }
111
112 public function filter_excerpt($content): string
113 {
114 if (!is_feed(self::feed_name())) return $content;
115
116 return $this->append_details($content, false);
117 }
118
119 private function append_details(string $content, bool $allow_html): string
120 {
121 global $post;
122 if (!$post instanceof WP_Post || $post->post_type !== LSD_Base::PTYPE_LISTING) return $content;
123
124 $content = trim($content);
125 if ($content === '')
126 {
127 $excerpt = get_post_field('post_excerpt', $post->ID);
128 if (is_string($excerpt) && trim($excerpt) !== '')
129 {
130 $excerpt = trim($excerpt);
131 if ($allow_html) $content = '<p>' . esc_html($excerpt) . '</p>';
132 else $content = wp_strip_all_tags($excerpt);
133 }
134 }
135
136 $details = $allow_html ? $this->details_html($post) : $this->details_text($post);
137 if ($details === '') return $content;
138
139 if ($content === '') return $details;
140 if ($allow_html) return $content . "\n" . $details;
141
142 return $content . "\n\n" . $details;
143 }
144
145 private function matches_preferred_path(): bool
146 {
147 $feed_base = isset($GLOBALS['wp_rewrite']->feed_base) ? (string) $GLOBALS['wp_rewrite']->feed_base : 'feed';
148 $feed_name = self::feed_name();
149
150 // Support query-string based feeds to avoid breaking backwards compatibility.
151 if (isset($_GET['feed']) && sanitize_title($_GET['feed']) === $feed_name) return true;
152
153 $request_uri = isset($_SERVER['REQUEST_URI']) ? wp_parse_url((string) $_SERVER['REQUEST_URI'], PHP_URL_PATH) : '';
154 if (!is_string($request_uri)) return true;
155
156 $request_uri = trim($request_uri, '/');
157 if ($request_uri === '') return true;
158
159 $segments = explode('/', $request_uri);
160 $base_index = array_search($feed_base, $segments, true);
161 if ($base_index === false) return false;
162
163 return isset($segments[$base_index + 1]) && $segments[$base_index + 1] === $feed_name;
164 }
165
166 private function details_html(WP_Post $post): string
167 {
168 $details = $this->collect_details($post);
169 if (!$details) return '';
170
171 $lines = [];
172 foreach ($details as $detail)
173 {
174 $value = $detail['value'];
175 if (!empty($detail['is_url'])) $value = '<a href="' . esc_url($value) . '">' . esc_html($value) . '</a>';
176 else $value = esc_html($value);
177
178 $lines[] = '<p><strong>' . esc_html($detail['label']) . ':</strong> ' . $value . '</p>';
179 }
180
181 return implode("\n", $lines);
182 }
183
184 private function details_text(WP_Post $post): string
185 {
186 $details = $this->collect_details($post);
187 if (!$details) return '';
188
189 $lines = [];
190 foreach ($details as $detail)
191 {
192 $value = $detail['value'];
193 if (!empty($detail['is_url'])) $value = esc_url($value);
194 else $value = sanitize_text_field($value);
195
196 $label = sanitize_text_field($detail['label']);
197 $lines[] = $label . ': ' . $value;
198 }
199
200 return implode("\n", $lines);
201 }
202
203 private function collect_details(WP_Post $post): array
204 {
205 $details = [];
206 $include_sensitive_details = $this->include_sensitive_details($post);
207
208 $address = get_post_meta($post->ID, 'lsd_address', true);
209 if ($include_sensitive_details && is_string($address) && trim($address) !== '')
210 {
211 $address_value = sanitize_text_field($address);
212 if ($address_value !== '')
213 {
214 $details[] = [
215 'label' => esc_html__('Address', 'listdom'),
216 'value' => $address_value,
217 ];
218 }
219 }
220
221 if (LSD_Components::pricing())
222 {
223 $min_price = get_post_meta($post->ID, 'lsd_price', true);
224 if ($min_price !== '' && $min_price !== null)
225 {
226 $currency = get_post_meta($post->ID, 'lsd_currency', true);
227 if (!is_string($currency) || trim($currency) === '') $currency = LSD_Options::currency();
228
229 $formatter = new LSD_Base();
230 $price_text = $formatter->render_price($min_price, $currency);
231
232 $max_price = get_post_meta($post->ID, 'lsd_price_max', true);
233 if (is_string($max_price) && trim($max_price) !== '')
234 {
235 $price_text .= ' - ' . $formatter->render_price($max_price, $currency);
236 }
237
238 $price_after = get_post_meta($post->ID, 'lsd_price_after', true);
239 if (is_string($price_after) && trim($price_after) !== '')
240 {
241 $price_text .= ' / ' . $price_after;
242 }
243
244 $details[] = [
245 'label' => esc_html__('Price', 'listdom'),
246 'value' => wp_strip_all_tags($price_text),
247 ];
248 }
249 }
250
251 $categories = get_the_terms($post, LSD_Base::TAX_CATEGORY);
252 if ($categories && !is_wp_error($categories))
253 {
254 $names = array_filter(array_map(static function ($term)
255 {
256 return isset($term->name) ? sanitize_text_field($term->name) : '';
257 }, $categories));
258
259 if ($names)
260 {
261 $details[] = [
262 'label' => esc_html__('Categories', 'listdom'),
263 'value' => implode(', ', $names),
264 ];
265 }
266 }
267
268 $phone = get_post_meta($post->ID, 'lsd_phone', true);
269 if ($include_sensitive_details && is_string($phone) && trim($phone) !== '')
270 {
271 $phone_value = sanitize_text_field($phone);
272 if ($phone_value !== '')
273 {
274 $details[] = [
275 'label' => esc_html__('Phone', 'listdom'),
276 'value' => $phone_value,
277 ];
278 }
279 }
280
281 $email = get_post_meta($post->ID, 'lsd_email', true);
282 if ($include_sensitive_details && is_string($email) && trim($email) !== '')
283 {
284 $email_value = sanitize_email($email);
285 if ($email_value !== '')
286 {
287 $details[] = [
288 'label' => esc_html__('Email', 'listdom'),
289 'value' => $email_value,
290 ];
291 }
292 }
293
294 $website = get_post_meta($post->ID, 'lsd_website', true);
295 if ($include_sensitive_details && is_string($website) && trim($website) !== '')
296 {
297 $website_value = esc_url_raw($website);
298 if ($website_value !== '')
299 {
300 $details[] = [
301 'label' => esc_html__('Website', 'listdom'),
302 'value' => $website_value,
303 'is_url' => true,
304 ];
305 }
306 }
307
308 return apply_filters('lsd_listings_feed_details', $details, $post);
309 }
310
311 private function include_sensitive_details(WP_Post $post): bool
312 {
313 $settings = LSD_Options::settings();
314 $include = isset($settings['rss_include_sensitive_details']) && $settings['rss_include_sensitive_details'];
315
316 return (bool) apply_filters('lsd_listings_feed_include_sensitive_details', $include, $post);
317 }
318 }
319