| 1 |
<?php |
| 2 |
/** |
| 3 |
* This template contains the author header on /friends/. |
| 4 |
* |
| 5 |
* @package Friends |
| 6 |
*/ |
| 7 |
|
| 8 |
$edit_user_link = $args['friends']->admin->admin_edit_user_link( false, $args['friend_user'] ); |
| 9 |
$_feeds = count( $args['friend_user']->get_feeds() ); |
| 10 |
$rules = count( $args['friend_user']->get_feed_rules() ); |
| 11 |
$active_feeds = count( $args['friend_user']->get_active_feeds() ); |
| 12 |
$hidden_post_count = $args['friend_user']->get_post_in_trash_count(); |
| 13 |
$display_name_html = apply_filters( 'friends_author_display_name_html', esc_html( $args['friend_user']->display_name ), $args['friend_user']->display_name, $args['friend_user'] ); |
| 14 |
|
| 15 |
// Get ActivityPub feeds and their data (header image, profile URLs, summary). |
| 16 |
$activitypub_feeds = array(); |
| 17 |
$seen_activitypub_feeds = array(); |
| 18 |
$header_image_url = null; |
| 19 |
$activitypub_summary = null; |
| 20 |
foreach ( $args['friend_user']->get_active_feeds() as $feed ) { |
| 21 |
// Check if this is an ActivityPub feed (parser is 'activitypub'). |
| 22 |
if ( 'activitypub' !== $feed->get_parser() ) { |
| 23 |
continue; |
| 24 |
} |
| 25 |
|
| 26 |
$ap_actor_id = $feed->get_ap_actor_id(); |
| 27 |
$ap_actor_url = $feed->get_ap_actor_url(); |
| 28 |
// The parser is only loaded while the ActivityPub plugin is active; a feed can still be marked as one without it. |
| 29 |
$ap_actor_acct = ''; |
| 30 |
if ( class_exists( 'Friends\Feed_Parser_ActivityPub' ) ) { |
| 31 |
$ap_actor_acct = Friends\Feed_Parser_ActivityPub::get_actor_acct_from_attributed_to( |
| 32 |
array( |
| 33 |
'ap_actor_id' => $ap_actor_id, |
| 34 |
) |
| 35 |
); |
| 36 |
} |
| 37 |
|
| 38 |
// If no actor URL from linked actor, use the feed URL as the actor URL. |
| 39 |
if ( ! $ap_actor_url ) { |
| 40 |
$ap_actor_url = $feed->get_url(); |
| 41 |
} |
| 42 |
|
| 43 |
if ( ! $ap_actor_url ) { |
| 44 |
continue; |
| 45 |
} |
| 46 |
|
| 47 |
$dedupe_key = $ap_actor_acct ? 'acct:' . strtolower( $ap_actor_acct ) : 'url:' . strtolower( untrailingslashit( $ap_actor_url ) ); |
| 48 |
if ( isset( $seen_activitypub_feeds[ $dedupe_key ] ) ) { |
| 49 |
continue; |
| 50 |
} |
| 51 |
$seen_activitypub_feeds[ $dedupe_key ] = true; |
| 52 |
|
| 53 |
$feed_data = array( |
| 54 |
'url' => $ap_actor_url, |
| 55 |
'acct' => $ap_actor_acct, |
| 56 |
); |
| 57 |
|
| 58 |
// Try to get actor metadata including header image and summary. |
| 59 |
if ( class_exists( '\Activitypub\Collection\Remote_Actors' ) ) { |
| 60 |
$actor = null; |
| 61 |
|
| 62 |
// First try using the linked actor ID. |
| 63 |
if ( $ap_actor_id ) { |
| 64 |
$actor = \Activitypub\Collection\Remote_Actors::get_actor( $ap_actor_id ); |
| 65 |
} |
| 66 |
|
| 67 |
// If no linked actor or it failed, try fetching by URL. |
| 68 |
if ( ( ! $actor || is_wp_error( $actor ) ) && method_exists( '\Activitypub\Collection\Remote_Actors', 'get_by_uri' ) ) { |
| 69 |
$actor_post = \Activitypub\Collection\Remote_Actors::get_by_uri( $ap_actor_url ); |
| 70 |
if ( $actor_post instanceof \WP_Post ) { |
| 71 |
$actor = \Activitypub\Collection\Remote_Actors::get_actor( $actor_post->ID ); |
| 72 |
} |
| 73 |
} |
| 74 |
|
| 75 |
if ( $actor && ! is_wp_error( $actor ) ) { |
| 76 |
$image = $actor->get_image(); |
| 77 |
if ( $image ) { |
| 78 |
$feed_data['header'] = \Activitypub\object_to_uri( $image ); |
| 79 |
// Use the first header image found. |
| 80 |
if ( ! $header_image_url ) { |
| 81 |
$header_image_url = $feed_data['header']; |
| 82 |
} |
| 83 |
} |
| 84 |
|
| 85 |
// Get summary/bio if we don't have one yet. |
| 86 |
if ( ! $activitypub_summary ) { |
| 87 |
$summary = $actor->get_summary(); |
| 88 |
if ( $summary ) { |
| 89 |
$activitypub_summary = $summary; |
| 90 |
} |
| 91 |
} |
| 92 |
} |
| 93 |
} |
| 94 |
|
| 95 |
$activitypub_feeds[] = $feed_data; |
| 96 |
} |
| 97 |
|
| 98 |
// Use ActivityPub summary as fallback for user description. |
| 99 |
$user_description = $args['friend_user']->description; |
| 100 |
if ( empty( $user_description ) && $activitypub_summary ) { |
| 101 |
$user_description = $activitypub_summary; |
| 102 |
} |
| 103 |
|
| 104 |
?><div id="author-header" class="mb-2<?php echo $header_image_url ? ' has-header-image' : ''; ?>" |
| 105 |
<?php |
| 106 |
if ( $header_image_url ) : |
| 107 |
?> |
| 108 |
style="background-image: url('<?php echo esc_url( $header_image_url ); ?>');"<?php endif; ?>> |
| 109 |
<h2 id="page-title"> |
| 110 |
<?php if ( $args['friend_user']->is_starred() ) : ?> |
| 111 |
<a href="" class="dashicons dashicons-star-filled starred" data-id="<?php echo esc_attr( $args['friend_user']->user_login ); ?>" data-nonce="<?php echo esc_attr( wp_create_nonce( 'star-' . $args['friend_user']->user_login ) ); ?>"></a> |
| 112 |
<?php else : ?> |
| 113 |
<a href="" class="dashicons dashicons-star-empty not-starred" data-id="<?php echo esc_attr( $args['friend_user']->user_login ); ?>" data-nonce="<?php echo esc_attr( wp_create_nonce( 'star-' . $args['friend_user']->user_login ) ); ?>"></a> |
| 114 |
<?php endif; ?> |
| 115 |
<a href="<?php echo esc_attr( $args['friend_user']->get_local_friends_page_url() ); ?>"> |
| 116 |
<?php |
| 117 |
if ( $args['friends']->frontend->reaction ) { |
| 118 |
echo esc_html( |
| 119 |
sprintf( |
| 120 |
// translators: %1$s is an emoji reaction, %2$s is a type of feed, e.g. "Main Feed". |
| 121 |
__( 'My %1$s reactions on %2$s', 'friends' ), |
| 122 |
$args['friends']->frontend->reaction, |
| 123 |
$args['friend_user']->display_name |
| 124 |
) |
| 125 |
); |
| 126 |
} else { |
| 127 |
if ( $args['friend_user']->get_avatar_url() ) { |
| 128 |
?> |
| 129 |
<img src="<?php echo esc_attr( $args['friend_user']->get_avatar_url() ); ?>" alt="<?php echo esc_attr( $args['friend_user']->display_name ); ?>" class="avatar" width="36" height="36" style="vertical-align: middle;" /> |
| 130 |
<?php |
| 131 |
} |
| 132 |
echo wp_kses_post( $display_name_html ); |
| 133 |
} |
| 134 |
?> |
| 135 |
</a> |
| 136 |
</h2> |
| 137 |
|
| 138 |
<?php if ( $user_description ) : ?> |
| 139 |
<p> |
| 140 |
<?php |
| 141 |
echo wp_kses( |
| 142 |
make_clickable( str_replace( '</p>', '<br/>', $user_description ) ), |
| 143 |
array( |
| 144 |
'a' => array( 'href' => array() ), |
| 145 |
'span' => array( 'class' => array() ), |
| 146 |
'br' => array(), |
| 147 |
) |
| 148 |
); |
| 149 |
?> |
| 150 |
</p> |
| 151 |
<?php endif; ?> |
| 152 |
|
| 153 |
<?php if ( apply_filters( 'friends_debug', false ) ) : ?> |
| 154 |
<span class="chip"><?php echo esc_html( get_class( $args['friend_user'] ) ); ?></span> |
| 155 |
<?php endif; ?> |
| 156 |
|
| 157 |
<span class="chip"><?php echo /* phpcs:ignore WordPress.WP.I18n.MissingArgDomain */ esc_html( sprintf( /* translators: %s is a localized date (F j, Y) */__( 'Since %s', 'friends' ), date_i18n( __( 'F j, Y' ), strtotime( $args['friend_user']->user_registered ) ) ) ); ?></span> |
| 158 |
|
| 159 |
<?php |
| 160 |
if ( ! empty( $args['friend_user']->user_url ) ) { |
| 161 |
$user_url_parts = wp_parse_url( $args['friend_user']->user_url ); |
| 162 |
if ( isset( $user_url_parts['host'] ) ) { |
| 163 |
$user_hostname = $user_url_parts['host']; |
| 164 |
if ( substr( $user_hostname, 0, 4 ) === 'www.' ) { |
| 165 |
$user_hostname = substr( $user_hostname, 4 ); |
| 166 |
} |
| 167 |
if ( isset( $user_url_parts['path'] ) && substr( $user_url_parts['path'], 0, 2 ) === '/@' ) { |
| 168 |
$p = strpos( $user_url_parts['path'], '/', 1 ); |
| 169 |
if ( false === $p ) { |
| 170 |
$p = strlen( $user_url_parts['path'] ); |
| 171 |
} |
| 172 |
$user_hostname .= esc_html( substr( $user_url_parts['path'], 0, $p ) ); |
| 173 |
} |
| 174 |
?> |
| 175 |
<a class="chip" href="<?php echo esc_url( $args['friend_user']->user_url ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $user_hostname ); ?></a> |
| 176 |
<?php |
| 177 |
} |
| 178 |
} |
| 179 |
?> |
| 180 |
|
| 181 |
<?php |
| 182 |
// Display ActivityPub profile links. |
| 183 |
foreach ( $activitypub_feeds as $ap_feed ) : |
| 184 |
$ap_url_parts = wp_parse_url( $ap_feed['url'] ); |
| 185 |
if ( isset( $ap_url_parts['host'] ) ) : |
| 186 |
$ap_hostname = $ap_url_parts['host']; |
| 187 |
if ( substr( $ap_hostname, 0, 4 ) === 'www.' ) { |
| 188 |
$ap_hostname = substr( $ap_hostname, 4 ); |
| 189 |
} |
| 190 |
// Extract @username for Mastodon-style URLs like /@username. |
| 191 |
$ap_display = $ap_hostname; |
| 192 |
if ( ! empty( $ap_feed['acct'] ) ) { |
| 193 |
$ap_display = '@' . $ap_feed['acct']; |
| 194 |
} elseif ( isset( $ap_url_parts['path'] ) && preg_match( '#^/@([^/]+)#', $ap_url_parts['path'], $matches ) ) { |
| 195 |
$ap_display = '@' . $matches[1] . '@' . $ap_hostname; |
| 196 |
} elseif ( isset( $ap_url_parts['path'] ) && preg_match( '#^/users/([^/]+)#i', $ap_url_parts['path'], $matches ) ) { |
| 197 |
$ap_display = '@' . $matches[1] . '@' . $ap_hostname; |
| 198 |
} |
| 199 |
?> |
| 200 |
<a class="chip activitypub-profile" href="<?php echo esc_url( $ap_feed['url'] ); ?>" target="_blank" rel="noopener" title="<?php esc_attr_e( 'ActivityPub Profile', 'friends' ); ?>"> |
| 201 |
<span class="dashicons dashicons-rss" style="font-size: 14px; width: 14px; height: 14px; margin-right: 4px; vertical-align: text-bottom;"></span><?php echo esc_html( $ap_display ); ?> |
| 202 |
</a> |
| 203 |
<span class="chip activitypub-follower-check is-loading" |
| 204 |
data-actor-url="<?php echo esc_attr( $ap_feed['url'] ); ?>" |
| 205 |
data-nonce="<?php echo esc_attr( wp_create_nonce( 'friends-check-follower' ) ); ?>" |
| 206 |
title="<?php esc_attr_e( 'Checking whether this actor follows you.', 'friends' ); ?>"> |
| 207 |
<?php esc_html_e( 'Checking follower...', 'friends' ); ?> |
| 208 |
</span> |
| 209 |
<?php |
| 210 |
endif; |
| 211 |
endforeach; |
| 212 |
?> |
| 213 |
|
| 214 |
<?php foreach ( $args['friend_user']->get_post_count_by_post_format() as $post_format => $count ) : ?> |
| 215 |
<a class="chip" href="<?php echo esc_attr( $args['friend_user']->get_local_friends_page_post_format_url( $post_format ) ); ?>"><?php echo esc_html( $args['friends']->get_post_format_plural_string( $post_format, $count ) ); ?></a> |
| 216 |
<?php endforeach; ?> |
| 217 |
<?php if ( isset( $_GET['show-hidden'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?> |
| 218 |
<a class="chip" href="<?php echo esc_attr( remove_query_arg( 'show-hidden' ) ); ?>"> |
| 219 |
<?php echo esc_html__( 'Hide hidden items', 'friends' ); ?> |
| 220 |
</a> |
| 221 |
<?php elseif ( $hidden_post_count > 0 ) : ?> |
| 222 |
<a class="chip" href="<?php echo esc_attr( add_query_arg( 'show-hidden', 1 ) ); ?>"> |
| 223 |
<?php echo esc_html( sprintf( /* translators: %s is the number of hidden posts */_n( '%s hidden items', '%s hidden items', $hidden_post_count, 'friends' ), number_format_i18n( $hidden_post_count ) ) ); ?> |
| 224 |
</a> |
| 225 |
<?php endif; ?> |
| 226 |
|
| 227 |
<?php foreach ( Friends\Reactions::get_available_emojis() as $slug => $reaction ) : ?> |
| 228 |
<a class="chip" href="<?php echo esc_attr( $args['friend_user']->get_local_friends_page_reaction_url( $slug ) ); ?>"> |
| 229 |
<?php |
| 230 |
echo esc_html( |
| 231 |
sprintf( |
| 232 |
// translators: %s is an emoji. |
| 233 |
__( 'Reacted with %s', 'friends' ), |
| 234 |
$reaction->char |
| 235 |
) |
| 236 |
); |
| 237 |
?> |
| 238 |
</a> |
| 239 |
<?php endforeach; ?> |
| 240 |
|
| 241 |
<?php if ( apply_filters( 'friends_show_author_edit', true, $args['friend_user'] ) ) : ?> |
| 242 |
<a class="chip" href="<?php echo esc_attr( self_admin_url( 'admin.php?page=edit-friend-feeds&user=' . $args['friend_user']->user_login ) ); ?>"> |
| 243 |
<?php echo esc_html( sprintf( /* translators: %s is the number of feeds */_n( '%s feed', '%s feeds', $active_feeds, 'friends' ), number_format_i18n( $active_feeds ) ) ); ?> |
| 244 |
|
| 245 |
<?php if ( $_feeds - $active_feeds > 1 ) : ?> |
| 246 |
<small><?php echo esc_html( sprintf( /* translators: %s is the number of feeds */_n( '(+%s more)', '(+%s more)', $_feeds - $active_feeds, 'friends' ), number_format_i18n( $_feeds - $active_feeds ) ) ); ?></small> |
| 247 |
<?php endif; ?> |
| 248 |
</a> |
| 249 |
|
| 250 |
<?php if ( $rules > 0 ) : ?> |
| 251 |
<a class="chip" href="<?php echo esc_attr( self_admin_url( 'admin.php?page=edit-friend-rules&user=' . $args['friend_user']->user_login . '&_wpnonce=' . wp_create_nonce( 'edit-friend-rules-' . $args['friend_user']->user_login ) ) ); ?>"> |
| 252 |
<?php |
| 253 |
// translators: %d is the number of rules. |
| 254 |
echo esc_html( sprintf( _n( '%d rule', '%d rules', $rules, 'friends' ), $rules ) ); |
| 255 |
?> |
| 256 |
</a> |
| 257 |
<?php endif; ?> |
| 258 |
|
| 259 |
<a class="chip" href="<?php echo esc_attr( $edit_user_link ); ?>"><?php /* phpcs:ignore WordPress.WP.I18n.MissingArgDomain */ esc_html_e( 'Edit' ); ?></a> |
| 260 |
<?php endif; ?> |
| 261 |
|
| 262 |
<?php if ( $args['friend_user'] instanceof \Friends\Subscription ) : ?> |
| 263 |
<?php |
| 264 |
$_folders = \Friends\Subscription::get_folders(); |
| 265 |
$_current_folder = $args['friend_user']->get_folder(); |
| 266 |
$_current_id = $_current_folder ? $_current_folder->term_id : 0; |
| 267 |
$_folder_nonce = wp_create_nonce( 'friends-move-to-folder' ); |
| 268 |
?> |
| 269 |
<span class="chip friends-folder-selector"> |
| 270 |
📁 <select class="friends-move-to-folder" data-id="<?php echo esc_attr( $args['friend_user']->user_login ); ?>" data-nonce="<?php echo esc_attr( $_folder_nonce ); ?>"> |
| 271 |
<option value="0"<?php selected( $_current_id, 0 ); ?>><?php esc_html_e( 'No folder', 'friends' ); ?></option> |
| 272 |
<?php foreach ( $_folders as $_folder ) : ?> |
| 273 |
<option value="<?php echo esc_attr( $_folder->term_id ); ?>"<?php selected( $_current_id, $_folder->term_id ); ?>><?php echo esc_html( $_folder->name ); ?></option> |
| 274 |
<?php endforeach; ?> |
| 275 |
<option value="new"><?php esc_html_e( '+ New folder', 'friends' ); ?></option> |
| 276 |
</select> |
| 277 |
</span> |
| 278 |
<?php endif; ?> |
| 279 |
|
| 280 |
<?php if ( $args['friend_user']->can_refresh_feeds() ) : ?> |
| 281 |
<a class="chip refresh-feeds" href="" data-user="<?php echo esc_attr( $args['friend_user']->user_login ); ?>" data-nonce="<?php echo esc_attr( wp_create_nonce( 'friends-refresh' ) ); ?>"><?php esc_html_e( 'Refresh', 'friends' ); ?></a> |
| 282 |
<?php endif; ?> |
| 283 |
|
| 284 |
<a class="chip toggle-compact" href=""><?php echo esc_html( 'collapsed' === $args['frontend_default_view'] ? __( 'Expanded mode', 'friends' ) : __( 'Compact mode', 'friends' ) ); ?></a> |
| 285 |
|
| 286 |
<?php do_action( 'friends_author_header', $args['friend_user'], $args ); ?> |
| 287 |
</div> |
| 288 |
<?php if ( $header_image_url ) : ?> |
| 289 |
<script> |
| 290 |
( function() { |
| 291 |
var header = document.getElementById( 'author-header' ); |
| 292 |
if ( ! header ) return; |
| 293 |
var titleRow = header.closest( '.mastodon-col-header' ); |
| 294 |
var navbar = header.closest( 'header' ) || ( titleRow ? titleRow.querySelector( '.mastodon-col-title-row' ) : null ); |
| 295 |
if ( ! navbar ) return; |
| 296 |
navbar.classList.add( 'has-header-image' ); |
| 297 |
navbar.style.backgroundImage = header.style.backgroundImage; |
| 298 |
header.style.backgroundImage = ''; |
| 299 |
header.classList.remove( 'has-header-image' ); |
| 300 |
} )(); |
| 301 |
</script> |
| 302 |
<?php endif; ?> |
| 303 |
|