' . '' . '' . '' . ''; } /** * Wordmark (bird + "Beep!") shown at the top of the feed. */ function beep_logo_svg() { // Allow site-owners to swap in a custom logo via settings. $custom = class_exists('Beep_Settings') ? Beep_Settings::get( 'logo_url' ) : ''; if ( $custom ) { return ''; } return ''; } /** * The "Beep!" brand wordmark shown at the bottom-right of every beep and reply. * * Rendered as a crisp inline SVG recreation of the 3D extruded brand logo — * a blue-violet face with a darker-blue depth and navy outline. * To swap in the PNG here instead, hook the filter: * add_filter('beep_post_wordmark', fn() => ''); */ function beep_post_wordmark() { // Build the 3D extrusion: stacked copies offset down-left, deepest first. $depth = 7; $extrude = ''; for ( $i = $depth; $i >= 1; $i-- ) { $x = 9 - $i; $y = 49 + $i; $extrude .= 'Beep!'; } $svg = '' . '' . $extrude . 'Beep!' . ''; return apply_filters('beep_post_wordmark', $svg); } /** * Resolve the avatar URL for a user: custom beep_avatar meta, then Gravatar, * then the brand bird as a last resort. * * @return array [ $url, $css_filter ] */ function beep_avatar_url( $user_id ) { $user_id = (int) $user_id; if ( $user_id ) { $custom = get_user_meta( $user_id, 'beep_avatar', true ); if ( $custom ) { return [ $custom, '' ]; } $user = get_userdata( $user_id ); if ( $user && ! empty( $user->user_email ) ) { return [ 'https://www.gravatar.com/avatar/' . md5( strtolower( $user->user_email ) ) . '?s=96&d=mp', '' ]; } } return [ BEEP_URL . 'assets/beep-bird.png', 'brightness(0) saturate(100%) invert(22%) sepia(100%) saturate(10000%) hue-rotate(190deg)', ]; } /** * Render a circular avatar for a user. * * @param int $user_id User ID (0 = anonymous → brand bird). * @param string $class Extra class, e.g. 'beep-avatar-sm'. */ function beep_avatar_img( $user_id, $class = '' ) { list( $url, $filter ) = beep_avatar_url( $user_id ); $classes = trim( 'beep-avatar ' . $class ); $style = $filter ? ' style="filter:' . esc_attr( $filter ) . ';"' : ''; return ''; } /** * Is this user banned from posting/liking/replying? */ function beep_user_is_banned($user_id) { if (!$user_id) { return false; } return get_user_meta($user_id, 'beep_banned', true) === '1'; } /** * Render the composer textarea (for logged-in, non-banned users). */ function beep_render_composer() { if ( ! is_user_logged_in() || beep_user_is_banned( get_current_user_id() ) ) { return ''; } $avatar = beep_avatar_img( get_current_user_id() ); ob_start(); ?>
$opt) : $pct = $total > 0 ? round(((int) ($votes[$i] ?? 0)) * 100 / $total) : 0; $has_voted = false; // kept simple for v1.9.0; full voting comes in v1.9.1 ?>
%
post_author); $avatar = beep_avatar_img($post->post_author); $name = $author ? esc_html($author->display_name) : 'Unknown'; $handle = $author ? '@' . esc_html($author->user_login) : ''; $ts = strtotime($post->post_date_gmt . ' UTC'); $time = beep_relative_time($ts); $time_iso = gmdate('c', $ts); $time_full = mysql2date('F j, Y g:i a', $post->post_date); $content = beep_format_content($post->post_content); $like_count = Beep_Likes::get_count($post->ID, 'post'); $reply_count = Beep_Replies::reply_count($post->ID); $user_liked = Beep_Likes::user_has_liked($post->ID, 'post'); $can_delete = current_user_can('delete_post', $post->ID); $can_interact = is_user_logged_in() && !beep_user_is_banned(get_current_user_id()); $beep_images = Beep_Media::get_images($post->ID); $beep_video = Beep_Media::get_video_url($post->ID); $beep_voice = Beep_Media::get_voice_url($post->ID); ob_start(); ?>
>
· 18, 'class' => 'beep-post-badge'] ); ?>
post_content); ?>
ID); ?>
ID); ?>
ID); ?>
ID, 'beep_gif_url', true); ?>
GIF
ID); ?>
ID, 0); ?>
ID); echo beep_render_replies_tree($all, 0, 0, $post->ID, $can_interact && $in_thread); ?>
%
· Final results Ends
>
comment_parent !== (int) $parent_id) { continue; } $out .= beep_render_reply($c, $comments, $depth, $post_id, $can_interact); } return $out; } /** * Render a single reply, plus its nested children. */ function beep_render_reply($comment, $all_comments = [], $depth = 0, $post_id = 0, $can_interact = false) { if (!$comment) { return ''; } $author_id = (int) $comment->user_id; $avatar = beep_avatar_img($author_id, 'beep-avatar-sm'); $author = $author_id ? get_userdata($author_id) : null; $name = $author ? esc_html($author->display_name) : esc_html($comment->comment_author); $handle = $author ? '@' . esc_html($author->user_login) : ''; $ts = strtotime($comment->comment_date_gmt . ' UTC'); $time = beep_relative_time($ts); $time_iso = gmdate('c', $ts); $content = beep_format_content($comment->comment_content); $can_delete = current_user_can('moderate_comments') || ($author_id && $author_id === get_current_user_id()); $like_count = Beep_Likes::get_count($comment->comment_ID, 'reply'); $user_liked = Beep_Likes::user_has_liked($comment->comment_ID, 'reply'); // Reply media (v1.9.0) $reply_images = Beep_Replies::get_reply_images($comment->comment_ID); $reply_voice = Beep_Replies::get_reply_voice_url($comment->comment_ID); $reply_gif = Beep_Replies::get_reply_gif_url($comment->comment_ID); $reply_quote_id = Beep_Replies::get_reply_quoted_post_id($comment->comment_ID); $reply_quote = $reply_quote_id ? get_post($reply_quote_id) : null; $depth_class = 'beep-depth-' . min((int) $depth, 2); ob_start(); ?>
·
comment_content); ?>
Voice note
post_type === 'beep') : ?>
ID); if ($quoted_data) echo Beep_Quotes::render_quoted_post($reply_quote->ID); ?>
GIF
comment_ID); ?>
comment_ID); ?> comment_ID, $depth + 1, $post_id, $can_interact); if ($children) { echo '
' . $children . '
'; } ?>
#' . esc_html($m[2]) . ''; }, $text); // @mentions $text = preg_replace_callback('/(^|\s)@([a-zA-Z0-9_]+)/', function ($m) { $user = get_user_by('login', $m[2]); if ($user) { return $m[1] . '@' . esc_html($m[2]) . ''; } return $m[1] . '@' . esc_html($m[2]) . ''; }, $text); // URLs $text = preg_replace_callback('#(https?://[^\s<]+)#i', function ($m) { $url = esc_url($m[1]); $display = parse_url($url, PHP_URL_HOST); if (!$display) { $display = $url; } return '' . esc_html($display) . ''; }, $text); // Line breaks $text = nl2br($text); return $text; } /** * Extract the first YouTube video ID from a text string, if any. * Supports youtube.com/watch?v=, youtu.be/, shorts/, embed/, live/. * Returns the 11-char video ID or ''. */ function beep_youtube_id_from_text($text) { if (!preg_match_all('#(?' . '' . '' . ''; } function beep_relative_time($timestamp) { $now = time(); $diff = $now - $timestamp; if ($diff < 0) { $diff = 0; } if ($diff < 60) { return $diff . 's'; } if ($diff < 3600) { return floor($diff / 60) . 'm'; } if ($diff < 86400) { return floor($diff / 3600) . 'h'; } if ($diff < 604800) { return floor($diff / 86400) . 'd'; } return gmdate('M j', $timestamp); } function beep_icon($name) { $icons = [ 'reply' => '', 'retweet' => '', 'heart' => '', 'heart_filled' => '', 'share' => '', ]; return isset($icons[$name]) ? $icons[$name] : ''; } /** * Render the thread/conversation modal (sits outside individual posts). */ function beep_render_thread_modal() { return ''; } /** * Render the GIF picker modal. */ function beep_render_gif_modal() { return ''; }