Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
require_once WPTV_THEME_DIR . '/inc/content.php';
require_once WPTV_THEME_DIR . '/inc/publishing.php';
require_once WPTV_THEME_DIR . '/inc/open-graph.php';
require_once WPTV_THEME_DIR . '/inc/global-footer.php';
require_once WPTV_THEME_DIR . '/inc/redirects.php';
require_once WPTV_THEME_DIR . '/inc/maintenance.php';
require_once WPTV_THEME_DIR . '/inc/endpoints/api.php';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php
/**
* The WordPress.org global footer, from wordpress.org's footer endpoint.
*
* WordPress.com doesn't run wporg-mu-plugins, so the footer block itself isn't
* available here; the endpoint serves its markup to sites outside the network.
*
* @package WordPressTV
*/

declare( strict_types = 1 );

namespace WordPressTV\GlobalFooter;

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Registers the block the footer template part renders.
*
* @return void
*/
function register_block(): void {
register_block_type(
'wptv/global-footer',
array(
'api_version' => 3,
'title' => __( 'WordPress.org footer', 'wptv' ),
'category' => 'theme',
'supports' => array(
'autoRegister' => true,
'html' => false,
'inserter' => false,
),
'render_callback' => __NAMESPACE__ . '\render',
)
);
}
add_action( 'init', __NAMESPACE__ . '\register_block' );

/**
* Renders the global footer, with the styles it needs.
*
* @return string Footer markup, or an empty string when it was never fetched.
*/
function render(): string {
$footer = get_footer();
if ( '' === $footer['markup'] ) {
return '';
}

// Versioned by the fetch, so a new footer never meets a stylesheet the browser cached for the old one.
wp_enqueue_style( 'wporg-global-header-footer', 'https://s.w.org/wp-content/mu-plugins/pub-sync/blocks/global-header-footer/build/style.css', array(), (string) $footer['fetched'] );
// Core only loads block styles for blocks it renders, and the footer's social links aren't rendered here.
wp_enqueue_style( 'wp-block-social-links' );

return $footer['markup'];
}

/**
* Returns the cached footer, fetching it again after 12 hours.
*
* A failed fetch keeps the last footer and tries again 5 minutes later.
*
* @return array Footer markup, when it was fetched, and when it expires.
*/
function get_footer(): array {
$cached = get_option( 'wptv_global_footer' );
if ( is_array( $cached ) && $cached['expires'] > time() ) {
return $cached;
}

$markup = fetch_markup();
if ( null === $markup ) {
$footer = array(
'markup' => $cached['markup'] ?? '',
'fetched' => $cached['fetched'] ?? 0,
'expires' => time() + 5 * MINUTE_IN_SECONDS,
);
} else {
$footer = array(
'markup' => $markup,
'fetched' => time(),
'expires' => time() + 12 * HOUR_IN_SECONDS,
);
}
update_option( 'wptv_global_footer', $footer, false );

return $footer;
}

/**
* Fetches the footer from wordpress.org.
*
* @return string|null Footer markup, or null when the request fails.
*/
function fetch_markup(): ?string {
$response = wp_safe_remote_get( 'https://wordpress.org/wp-json/global-header-footer/v1/footer', array( 'timeout' => 3 ) );
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
return null;
}

return extract_footer( wp_remote_retrieve_body( $response ) );
}

/**
* Takes the footer element from the endpoint's response, as a div.
*
* The response goes on with wordpress.org's `wp_footer()` scripts and closes the
* document, so only the element is kept. The template part is the page's footer
* element, as on wordpress.org, where the block renders as a div inside it.
*
* @param string $body Response body.
* @return string|null Footer markup, or null when the body doesn't start with a footer element.
*/
function extract_footer( string $body ): ?string {
$body = ltrim( $body );
$end = strpos( $body, '</footer>' );
if ( ! str_starts_with( $body, '<footer ' ) || false === $end ) {
return null;
}

return '<div ' . substr( $body, strlen( '<footer ' ), $end - strlen( '<footer ' ) ) . '</div>';
}
43 changes: 1 addition & 42 deletions wordpress.tv/public_html/wp-content/themes/wptv-27/inc/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
function setup() {
add_editor_style( 'style.css' );

// Same location names as wptv2, so the assigned menus survive switching between both themes.
// Same location name as wptv2, so the assigned menu survives switching between both themes.
register_nav_menus(
array(
'primary' => __( 'Primary Menu', 'wptv' ),
'footer' => __( 'Footer Menu', 'wptv' ),
)
);
}
Expand Down Expand Up @@ -84,46 +83,6 @@ function primary_menu_links( $parsed_block ) {
}
add_filter( 'render_block_data', __NAMESPACE__ . '\primary_menu_links' );

/**
* Fills the footer "Contribute" list with the classic menu assigned to `footer`.
*
* Only top-level items are shown, as wptv2's footer menu (depth 1). Links to
* other sites get the decorative arrow the footer design uses. Without an
* assigned menu, or with an empty one, the pattern's own links are kept.
*
* @param string $content Rendered List block.
* @param array $block Parsed block.
* @return string
*/
function footer_menu_links( $content, $block ) {
$classes = preg_split( '/\s+/', $block['attrs']['className'] ?? '' );
if ( ! in_array( 'wptv-footer__contribute', $classes, true ) || ! preg_match( '/^\s*(<ul\b[^>]*>)/', $content, $opening ) ) {
return $content;
}

$locations = get_nav_menu_locations();
$menu = wp_get_nav_menu_object( $locations['footer'] ?? 0 );
$items = $menu ? wp_get_nav_menu_items( $menu->term_id ) : array();
if ( ! $items ) {
return $content;
}

$home_host = wp_parse_url( home_url(), PHP_URL_HOST );
$links = '';
foreach ( $items as $item ) {
if ( (int) $item->menu_item_parent ) {
continue;
}
$host = wp_parse_url( $item->url, PHP_URL_HOST );
$arrow = ( $host && $host !== $home_host ) ? ' <span aria-hidden="true">↗</span>' : '';
$target = $item->target ? ' target="' . esc_attr( $item->target ) . '" rel="noopener"' : '';
$links .= '<li><a href="' . esc_url( $item->url ) . '"' . $target . '>' . esc_html( $item->title ) . $arrow . '</a></li>';
}

return $links ? $opening[1] . $links . '</ul>' : $content;
}
add_filter( 'render_block_core/list', __NAMESPACE__ . '\footer_menu_links', 10, 2 );

/**
* Registers the recurring visual treatments used by theme patterns.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!-- wp:pattern {"slug":"wptv/footer"} /-->
<!-- wp:wptv/global-footer /-->

This file was deleted.

20 changes: 3 additions & 17 deletions wordpress.tv/public_html/wp-content/themes/wptv-27/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ img, video, iframe { max-width: 100%; }
.wp-site-blocks > * + * { margin-block-start: 0; }
.wp-site-blocks main { padding-block: var(--wp--preset--spacing--60); }
.wp-site-blocks main.is-flush { padding-block: 0; }
.wp-site-blocks main.wptv-single { padding-block-end: var(--wp--preset--spacing--60); }
:where(.wp-block-group, .wp-block-column, .wp-block-post, .wp-block-query) { min-width: 0; }
:where(input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]), textarea, select) {
box-sizing: border-box; max-width: 100%; padding: 8px 15px; border: 1px solid var(--wp--preset--color--charcoal-4); border-radius: 2px;
Expand Down Expand Up @@ -70,21 +71,8 @@ img, video, iframe { max-width: 100%; }
.wptv-header .is-menu-open .wp-block-navigation__container { align-items: stretch; width: 100%; gap: 0; }
.wptv-header .is-menu-open .wp-block-navigation-item { padding-block: 12px; border-bottom: 1px solid var(--wp--preset--color--light-grey-1); }
.wptv-header .is-menu-open .wp-block-search { width: 100%; }
.wptv-footer { padding-block: var(--wp--preset--spacing--50) var(--wp--preset--spacing--30); font-size: var(--wp--preset--font-size--small); }
.wptv-footer a { color: var(--wp--preset--color--white); text-decoration: none; }
.wptv-footer a:hover { text-decoration: underline; }
.wptv-footer a:focus { outline-color: var(--wp--preset--color--blueberry-2); }
.wptv-footer__heading { font: 600 var(--wp--preset--font-size--small)/1.625 var(--wp--preset--font-family--inter); color: var(--wp--preset--color--white); }
.wptv-footer__columns { gap: 30px; }
.wptv-footer__columns > .wp-block-column:first-child { flex-grow: 2; }
.wptv-footer__links { list-style: none; padding: 0; }
.wptv-footer__links li + li { margin-top: 8px; }
.wptv-footer__license { border-top: 1px solid var(--wp--preset--color--white-opacity-15); margin-top: var(--wp--preset--spacing--40); padding-top: var(--wp--preset--spacing--20); gap: 20px; }
.wptv-footer .wp-block-site-title { font-family: var(--wp--preset--font-family--inter); font-size: var(--wp--preset--font-size--large); }
.wptv-footer__wporg { margin: 0; }
.wptv-footer .wp-block-site-title + p,
.wptv-footer .wptv-footer__wporg + p { margin-block-start: 0; }
.wptv-footer__wporg img { display: block; width: auto; max-width: 100%; height: 27px; }
.global-footer a:focus { outline-color: var(--wp--preset--color--blueberry-2); }
.global-footer a[href="https://wordpress.tv/"] [aria-hidden="true"] { display: none; }
/* === cards === */
.is-style-cards-grid { gap: var(--wp--preset--spacing--20); }
.is-style-cards-grid > :where(li, .wp-block-group) { position: relative; border: 1px solid var(--wp--preset--color--light-grey-1); border-radius: 2px; box-sizing: border-box; overflow-wrap: anywhere; padding: 20px; }
Expand Down Expand Up @@ -230,8 +218,6 @@ img, video, iframe { max-width: 100%; }
html { scroll-padding-top: 20px; }
}
@media (max-width: 781px) {
.wptv-footer .wptv-footer__columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wptv-footer__columns > .wp-block-column:first-child { grid-column: 1 / -1; }
.wptv-home__section-heading { align-items: start; }
}
@media (max-width: 599px) {
Expand Down
Loading