PluginProbe
Friends / 2.8.9
Friends v2.8.9
4.3.2 4.3.1 4.3.0 4.2.2 4.2.1 4.2.0 4.1.0 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.0 2.9.1 All 88 releases
friends / feed-parsers / class-feed-parser-microformats.php

class-feed-parser-microformats.php in Friends 2.8.9, at feed-parsers/class-feed-parser-microformats.php

350 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Friends microformats Parser Wrapper
4 *
5 * With this parser, we can import RSS and Atom Feeds for a friend.
6 *
7 * @package Friends
8 */
9
10 namespace Friends;
11
12 /**
13 * This is the class for the feed part of the Friends Plugin.
14 *
15 * @since 1.0
16 *
17 * @package Friends
18 * @author Alex Kirk
19 */
20 class Feed_Parser_Microformats extends Feed_Parser_V2 {
21 const NAME = 'Microformats';
22 const URL = 'https://www.microformats.org/';
23 const SLUG = 'microformats';
24
25 /**
26 * Determines if this is a supported feed and to what degree we feel it's supported.
27 *
28 * @param string $url The url.
29 * @param string $mime_type The mime type.
30 * @param string $title The title.
31 * @param string|null $content The content, it can't be assumed that it's always available.
32 *
33 * @return int Return 0 if unsupported, a positive value representing the confidence for the feed, use 10 if you're reasonably confident.
34 */
35 public function feed_support_confidence( $url, $mime_type, $title, $content = null ) {
36 if ( ! $content ) {
37 return 0;
38 }
39 $feeds = self::discover_available_feeds( $content, $url );
40 if ( isset( $feeds[ $url ] ) ) {
41 return 10;
42 }
43
44 return 0;
45 }
46
47 /**
48 * Format the feed title and autoselect the posts feed.
49 *
50 * @param array $feed_details The feed details.
51 *
52 * @return array The (potentially) modified feed details.
53 */
54 public function update_feed_details( $feed_details ) {
55 $feed_details['title'] = trim( str_replace( array( '&raquo; Feed', '» Feed' ), '', $feed_details['title'] ) );
56
57 foreach ( get_post_format_strings() as $format => $title ) {
58 if ( preg_match( '/\b' . preg_quote( $format, '/' ) . '\b/i', $feed_details['url'] ) ) {
59 $feed_details['post-format'] = $format;
60 break;
61 }
62 }
63
64 return $feed_details;
65 }
66
67 /**
68 * Discover the feeds available at the URL specified.
69 *
70 * @param string $content The content for the URL is already provided here.
71 * @param string $url The url to search.
72 *
73 * @return array A list of supported feeds at the URL.
74 */
75 public function discover_available_feeds( $content, $url ) {
76 $discovered_feeds = array();
77 $mf = Mf2\parse( $content, $url );
78 if ( isset( $mf['rel-urls'] ) ) {
79 foreach ( $mf['rel-urls'] as $feed_url => $link ) {
80 foreach ( array( 'me', 'alternate' ) as $rel ) {
81 if ( in_array( $rel, $link['rels'] ) ) {
82 $discovered_feeds[ $feed_url ] = array(
83 'rel' => $rel,
84 );
85 }
86 }
87
88 if ( ! isset( $discovered_feeds[ $feed_url ] ) ) {
89 continue;
90 }
91
92 if ( isset( $link['type'] ) ) {
93 $discovered_feeds[ $feed_url ]['type'] = $link['type'];
94 }
95
96 if ( isset( $link['title'] ) ) {
97 $discovered_feeds[ $feed_url ]['title'] = $link['title'];
98 } elseif ( isset( $link['text'] ) ) {
99 $discovered_feeds[ $feed_url ]['title'] = $link['text'];
100 }
101 }
102 }
103
104 if ( isset( $mf['items'] ) ) {
105 $feed_items = $this->parse_hfeed( $mf );
106 if ( count( $feed_items ) > 0 ) {
107 $discovered_feeds[ $url ] = array(
108 'type' => 'text/html',
109 'rel' => 'self',
110 'post-format' => 'autodetect',
111 'parser' => 'microformats',
112 );
113 }
114 }
115
116 return $discovered_feeds;
117 }
118
119 /**
120 * Parse an h-card.
121 *
122 * @param array $data The data.
123 * @param bool $category It is a category.
124 *
125 * @return string The discovered value.
126 */
127 private function parse_hcard( $data, $category = false ) {
128 $name = '';
129 $link = '';
130 // Check if h-card is set and pass that information on in the link.
131 if ( isset( $data['type'] ) && in_array( 'h-card', $data['type'] ) ) {
132 if ( isset( $data['properties']['name'][0] ) ) {
133 $name = $data['properties']['name'][0];
134 }
135 if ( isset( $data['properties']['url'][0] ) ) {
136 $link = $data['properties']['url'][0];
137 if ( '' === $name ) {
138 $name = $link;
139 } else {
140 // can't have commas in categories.
141 $name = str_replace( ',', '', $name );
142 }
143 $person_tag = $category ? '<span class="person-tag"></span>' : '';
144 return '<a class="h-card" href="' . $link . '">' . $person_tag . $name . '</a>';
145 }
146 }
147 return isset( $data['value'] ) ? $data['value'] : '';
148 }
149
150 /**
151 * Parse an h-feed.
152 *
153 * @param array $mf The microfeeds2 parser response.
154 *
155 * @return array An array of feed items.
156 */
157 public function parse_hfeed( $mf ) {
158 if ( ! is_array( $mf ) ) {
159 return array();
160 }
161 $feed_items = array();
162 $entries = array();
163
164 // The following section is adapted from the SimplePie source.
165 // 2020-11-30: Modifications: set post format, handle videos.
166 // This should implement the https://www.w3.org/TR/post-type-discovery/ algorithm.
167
168 $feed_author = null;
169 $h_feed = array();
170 foreach ( $mf['items'] as $mf_item ) {
171 if ( in_array( 'h-feed', $mf_item['type'] ) ) {
172 $h_feed = $mf_item;
173 break;
174 }
175 // Also look for h-feed or h-entry in the children of each top level item.
176 if ( ! isset( $mf_item['children'][0]['type'] ) ) {
177 continue;
178 }
179 if ( in_array( 'h-feed', $mf_item['children'][0]['type'] ) ) {
180 $h_feed = $mf_item['children'][0];
181 // In this case the parent of the h-feed may be an h-card, so use it as
182 // the feed_author.
183 if ( in_array( 'h-card', $mf_item['type'] ) ) {
184 $feed_author = $mf_item;
185 }
186 break;
187 } elseif ( in_array( 'h-entry', $mf_item['children'][0]['type'] ) ) {
188 $entries = $mf_item['children'];
189 // In this case the parent of the h-entry list may be an h-card, so use
190 // it as the feed_author.
191 if ( in_array( 'h-card', $mf_item['type'] ) ) {
192 $feed_author = $mf_item;
193 }
194 break;
195 }
196 }
197
198 if ( isset( $h_feed['children'] ) ) {
199 $entries = $h_feed['children'];
200 } elseif ( empty( $entries ) ) {
201 $entries = $mf['items'];
202 }
203
204 foreach ( $entries as $entry ) {
205 if ( isset( $entry['properties']['deleted'][0] ) || ! isset( $entry['properties']['published'][0] ) ) {
206 continue;
207 }
208 $item = new Feed_Item();
209 $item->date = $entry['properties']['published'][0];
210 $item->author = $feed_author;
211
212 if ( isset( $entry['properties']['url'][0] ) ) {
213 $link = $entry['properties']['url'][0];
214 if ( isset( $link['value'] ) ) {
215 $link = $link['value'];
216 }
217 $item->permalink = $link;
218 }
219
220 if ( isset( $entry['properties']['uid'][0] ) ) {
221 $guid = $entry['properties']['uid'][0];
222 if ( isset( $guid['value'] ) ) {
223 $guid = $guid['value'];
224 }
225 $item->permalink = $guid;
226 }
227
228 if ( isset( $entry['properties']['name'][0] ) ) {
229 $title = $entry['properties']['name'][0];
230 if ( isset( $title['value'] ) ) {
231 $title = $title['value'];
232 }
233 $item->title = $title;
234 }
235
236 $content = '';
237 foreach ( array( 'photo', 'video' ) as $media ) {
238 if ( ! isset( $entry['properties'][ $media ][0] ) ) {
239 continue;
240 }
241
242 // If a $media is also in content, don't need to add it again here.
243 if ( isset( $entry['properties']['content'][0]['html'] ) ) {
244 $content = $entry['properties']['content'][0]['html'];
245 }
246 $el_list = array();
247 for ( $j = 0; $j < count( $entry['properties'][ $media ] ); $j++ ) {
248 $el = $entry['properties'][ $media ][ $j ];
249 if ( ! empty( $el ) && strpos( $content, $el ) === false ) {
250 $el_list[] = $el;
251 }
252 }
253
254 if ( count( $el_list ) > 0 ) {
255 $set_id = preg_replace( '/[[:^alnum:]]/', '', $el_list[0] );
256 $content = '<p>';
257 foreach ( $el_list as $j => $el ) {
258 $hidden = 0 === $j ? '' : 'class="hidden" ';
259 $content .= '<a href="' . $el . '" ' . $hidden .
260 'data-lightbox="set-' . $set_id . '">';
261 switch ( $media ) {
262 case 'photo':
263 $content .= '<img src="' . esc_url( $el ) . '" />';
264 break;
265 case 'video':
266 $content .= '<video src="' . esc_url( $el ) . '" />';
267 break;
268 }
269 $content .= '</a>';
270 }
271 $content .= '<br><b>';
272 switch ( $media ) {
273 case 'photo':
274 // translators: %s is the number of photos.
275 $content .= sprintf( _n( '%d photo', '%d photos', count( $el_list ), 'friends' ), count( $el_list ) );
276 break;
277 case 'video':
278 // translators: %s is the number of videos.
279 $content .= sprintf( _n( '%d video', '%d videos', count( $el_list ), 'friends' ), count( $el_list ) );
280 break;
281 }
282 $content .= '</b></p>';
283 }
284 $item->format = $media;
285 }
286
287 if ( isset( $entry['properties']['content'][0]['html'] ) ) {
288 $content .= $entry['properties']['content'][0]['html'];
289 if ( isset( $entry['properties']['in-reply-to'][0] ) ) {
290 $in_reply_to = '';
291 if ( is_string( $entry['properties']['in-reply-to'][0] ) ) {
292 $in_reply_to = $entry['properties']['in-reply-to'][0];
293 } elseif ( isset( $entry['properties']['in-reply-to'][0]['value'] ) ) {
294 $in_reply_to = $entry['properties']['in-reply-to'][0]['value'];
295 }
296 if ( '' !== $in_reply_to ) {
297 $content .= '<p><span class="in-reply-to"></span> ' .
298 '<a href="' . $in_reply_to . '">' . $in_reply_to . '</a><p>';
299 }
300 }
301 $item->content = $content;
302 }
303
304 if ( isset( $entry['properties']['category'] ) ) {
305 $category_csv = '';
306 // Categories can also contain h-cards.
307 foreach ( $entry['properties']['category'] as $category ) {
308 if ( '' !== $category_csv ) {
309 $category_csv .= ', ';
310 }
311 if ( is_string( $category ) ) {
312 // Can't have commas in categories.
313 $category_csv .= str_replace( ',', '', $category );
314 } else {
315 $category_csv .= $this->parse_hcard( $category, true );
316 }
317 }
318 $item->category = $category_csv;
319 }
320
321 if ( ! $item->post_format && in_array( 'h-as-note', $entry['type'] ) ) {
322 $item->post_format = 'status';
323 $item->title = '';
324 }
325
326 $feed_items[] = $item;
327 }
328
329 return $feed_items;
330 }
331
332 /**
333 * Fetches a feed and returns the processed items.
334 *
335 * @param string $url The url.
336 * @param User_Feed $user_feed The user feed.
337 *
338 * @return array An array of feed items.
339 */
340 public function fetch_feed( $url, User_Feed $user_feed = null ) {
341 $mf = Mf2\fetch( $url );
342 if ( ! $mf ) {
343 // translators: %s is a URL.
344 return new \WP_Error( 'microformats Parser', sprintf( __( 'Could not parse %s.', 'friends' ), $url ) );
345 }
346
347 return $this->parse_hfeed( $mf );
348 }
349 }
350