@@ -82,6 +82,7 @@ public function get_timeline( $result, $channel, $args ) {
8282
8383 $ events = $ this ->get_events_items ( $ limit );
8484 $ combined = \array_merge ( $ news_items , $ events );
85+ $ combined = $ this ->dedupe_items_by_id ( $ combined );
8586 $ combined = $ this ->sort_items_by_date ( $ combined );
8687 $ result ['items ' ] = \array_merge ( $ result ['items ' ], \array_slice ( $ combined , 0 , $ limit ) );
8788
@@ -140,7 +141,8 @@ protected function get_news_items( $limit ) {
140141 require_once ABSPATH . WPINC . '/feed.php ' ;
141142 }
142143
143- $ feed = \fetch_feed ( $ this ->news_feed );
144+ $ feed_url = $ this ->get_news_feed_url ();
145+ $ feed = \fetch_feed ( $ feed_url );
144146
145147 if ( \is_wp_error ( $ feed ) ) {
146148 return array ();
@@ -169,6 +171,24 @@ protected function get_news_items( $limit ) {
169171 return $ list ;
170172 }
171173
174+ /**
175+ * Resolve the localized news feed URL based on the site locale.
176+ *
177+ * Mirrors the dashboard news widget behavior by using locale-specific domains when available.
178+ *
179+ * @return string
180+ */
181+ protected function get_news_feed_url () {
182+ $ locale = \function_exists ( 'get_locale ' ) ? \get_locale () : 'en_US ' ;
183+
184+ if ( empty ( $ locale ) || 'en_US ' === $ locale ) {
185+ return $ this ->news_feed ;
186+ }
187+
188+ $ subdomain = \strtolower ( \str_replace ( '_ ' , '- ' , $ locale ) );
189+ return 'https:// ' . $ subdomain . '.wordpress.org/news/feed/ ' ;
190+ }
191+
172192 /**
173193 * Read WordPress community events.
174194 *
@@ -184,9 +204,12 @@ protected function get_events_items( $limit ) {
184204 return array ();
185205 }
186206
207+ $ user_id = \get_current_user_id ();
208+
187209 $ events_response = \wp_get_community_events (
188210 array (
189211 'number ' => $ limit ,
212+ 'location ' => $ this ->get_events_location ( $ user_id ),
190213 )
191214 );
192215
@@ -214,6 +237,32 @@ protected function get_events_items( $limit ) {
214237 return $ items ;
215238 }
216239
240+ /**
241+ * Resolve the community events location (user preference or filtered override).
242+ *
243+ * @param int $user_id Current user ID.
244+ * @return array Location array as accepted by wp_get_community_events.
245+ */
246+ protected function get_events_location ( $ user_id ) {
247+ $ location = array ();
248+
249+ if ( $ user_id ) {
250+ $ user_location = \get_user_option ( 'community-events-location ' , $ user_id );
251+
252+ if ( \is_array ( $ user_location ) ) {
253+ $ location = $ user_location ;
254+ }
255+ }
256+
257+ /**
258+ * Filter the location used for WordPress Events.
259+ *
260+ * @param array $location Location array.
261+ * @param int $user_id Current user ID.
262+ */
263+ return \apply_filters ( 'microsub_events_location ' , $ location , $ user_id );
264+ }
265+
217266 /**
218267 * Sort items by published date (newest first).
219268 *
@@ -232,4 +281,31 @@ function ( $a, $b ) {
232281
233282 return $ items ;
234283 }
284+
285+ /**
286+ * Deduplicate items by their _id key.
287+ *
288+ * @param array $items Items to filter.
289+ * @return array
290+ */
291+ protected function dedupe_items_by_id ( $ items ) {
292+ $ unique = array ();
293+ $ seen = array ();
294+
295+ foreach ( $ items as $ item ) {
296+ $ id = isset ( $ item ['_id ' ] ) ? $ item ['_id ' ] : null ;
297+
298+ if ( $ id && isset ( $ seen [ $ id ] ) ) {
299+ continue ;
300+ }
301+
302+ if ( $ id ) {
303+ $ seen [ $ id ] = true ;
304+ }
305+
306+ $ unique [] = $ item ;
307+ }
308+
309+ return $ unique ;
310+ }
235311}
0 commit comments