From a04e6894426e51d6bd470c6847b06b0714074461 Mon Sep 17 00:00:00 2001 From: Aigars Silkalns Date: Tue, 11 Aug 2026 10:18:00 +0300 Subject: [PATCH 1/4] Guard the related-posts term lookups against WP_Error wp_get_object_terms() returns a WP_Error when the taxonomy is not registered, which happens whenever whatever supplied the portfolio post type is deactivated while its posts remain. ! empty() is true for an object, so the WP_Error was pushed straight into tax_query and WP_Query then returned the wrong related posts rather than none. Not fatal here -- unlike the same pattern in the companion plugin's Portfolio widget, which raised a TypeError in implode() and truncated the page -- but wrong in the same way and worth closing off. Co-Authored-By: Claude Opus 5 (1M context) --- inc/class-shapely-related-posts.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/inc/class-shapely-related-posts.php b/inc/class-shapely-related-posts.php index 71824d0..b35a38f 100644 --- a/inc/class-shapely-related-posts.php +++ b/inc/class-shapely-related-posts.php @@ -143,6 +143,20 @@ public function get_related_posts( $post_id, $number_posts = - 1 ) { $types = wp_get_object_terms( get_the_ID(), 'jetpack-portfolio-type', $terms_args ); $tags = wp_get_object_terms( get_the_ID(), 'jetpack-portfolio-tag', $terms_args ); + /* + * These return WP_Error when the taxonomy is not registered, which + * happens whenever whatever supplied the portfolio post type is + * deactivated while its posts remain. ! empty() is true for an + * object, so without this the WP_Error was pushed straight into + * tax_query and WP_Query silently returned the wrong posts. + */ + if ( is_wp_error( $types ) ) { + $types = array(); + } + if ( is_wp_error( $tags ) ) { + $tags = array(); + } + $tax_query = array(); if ( ! empty( $types ) ) { From 9d2b20d7895577725e2e9f221d0527b234f99da0 Mon Sep 17 00:00:00 2001 From: Aigars Silkalns Date: Tue, 11 Aug 2026 10:19:10 +0300 Subject: [PATCH 2/4] Load Font Awesome under a theme-specific handle WordPress deduplicates enqueues by handle: whichever caller registers a handle first wins, and every later enqueue of the same handle is silently a no-op. The theme registered its Font Awesome 6 as plain 'font-awesome', which is the same handle Elementor uses for the Font Awesome 4.7 it bundles. On any site running Elementor -- including the official demo -- Elementor registers first, so the theme's Font Awesome 6 never loaded. The theme then rendered fa-brands / fa-solid / fa-x-twitter / fa-mobile-screen against Font Awesome 4, where none of those classes exist, and the social, search and menu icons came out as blank boxes. Verified on the demo's markup: id='font-awesome-css' href='.../elementor/.../font-awesome.min.css?ver=4.7.0' and the theme's own fontawesome6/all.min.css absent from the page entirely while every other theme stylesheet loaded normally. Renaming to 'shapely-font-awesome' means the theme always gets the version its markup is written against, whatever plugins are installed. Note for child themes: anything calling wp_dequeue_style( 'font-awesome' ) to swap the icon set now needs the new handle. The theme's other generic handles ('bootstrap', 'flexslider', 'owl.carousel') carry the same collision risk but are left alone here -- they are not currently broken anywhere, and renaming them would break dequeues for no present benefit. Co-Authored-By: Claude Opus 5 (1M context) --- functions.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/functions.php b/functions.php index bca4574..0df6479 100644 --- a/functions.php +++ b/functions.php @@ -288,8 +288,16 @@ function shapely_scripts() { // Add Bootstrap default CSS wp_enqueue_style( 'bootstrap', $uri . '/assets/css/bootstrap.min.css', array(), '3.3.7' ); - // Replace old Font Awesome with Font Awesome 6 - wp_enqueue_style( 'font-awesome', $uri . '/assets/css/fontawesome6/all.min.css', array(), '6.4.2' ); + /* + * Registered under a theme-specific handle rather than the generic + * 'font-awesome'. WordPress deduplicates by handle, so whichever plugin + * registers 'font-awesome' first wins and every later enqueue is silently + * a no-op. Elementor ships Font Awesome 4.7 under exactly that handle, so + * on any site running it the theme's Font Awesome 6 never loaded at all -- + * and the theme's fa-brands / fa-solid classes do not exist in 4.x, which + * is why the social, search and menu icons rendered as blank boxes. + */ + wp_enqueue_style( 'shapely-font-awesome', $uri . '/assets/css/fontawesome6/all.min.css', array(), '6.4.2' ); // Add Google Fonts wp_enqueue_style( 'shapely-fonts', 'https://fonts.googleapis.com/css?family=Raleway:100,300,400,500,600,700&display=swap', array(), null ); From a359fc8678f71b5d982e077427a3664bf2773397 Mon Sep 17 00:00:00 2001 From: Aigars Silkalns Date: Tue, 11 Aug 2026 10:23:20 +0300 Subject: [PATCH 3/4] Fix the customizer's Font Awesome enqueue: wrong path and same handle collision The Epsilon framework enqueues Font Awesome for the customizer screens with wp_enqueue_style( 'font-awesome', EPSILON_URI . '../../assets/css/...' ); which is wrong twice over. EPSILON_URI is already trailingslashit'd to .../themes/shapely/inc/libraries/epsilon-framework/ so '../../' climbs to .../themes/shapely/inc/ -- one level short of the theme root. The resulting .../shapely/inc/assets/css/fontawesome6/all.min.css does not exist and never has, so the customizer has been 404ing on its own icon font. Confirmed against the working tree: the old path resolves to a file that is not there, the new one to the real 102 KB stylesheet. It also reuses the generic 'font-awesome' handle, so on any site where a plugin registers that handle first the enqueue is a silent no-op regardless of path. Now points at get_template_directory_uri() and shares the theme's 'shapely-font-awesome' handle, so the admin and front end load one file, once, with a real version string for cache busting. This library is vendored rather than a submodule, so it is patched in place -- same as the two existing PHP-compatibility patches in inc/libraries/. Co-Authored-By: Claude Opus 5 (1M context) --- .../epsilon-framework/class-epsilon-framework.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/inc/libraries/epsilon-framework/class-epsilon-framework.php b/inc/libraries/epsilon-framework/class-epsilon-framework.php index 8b802ad..313ef7c 100644 --- a/inc/libraries/epsilon-framework/class-epsilon-framework.php +++ b/inc/libraries/epsilon-framework/class-epsilon-framework.php @@ -259,7 +259,16 @@ public function customizer_enqueue_scripts() { 'row' => esc_html__( 'Row', 'shapely' ), ) ); - wp_enqueue_style( 'font-awesome', EPSILON_URI . '../../assets/css/fontawesome6/all.min.css' ); + /* + * Two faults here, both fixed in place (this library is vendored, not a + * submodule). EPSILON_URI already ends in .../inc/libraries/epsilon-framework/, + * so '../../' resolved to .../inc/assets/css/... -- one level short of the + * theme root, and a 404, which is why the customizer's own icons were + * missing. And the generic 'font-awesome' handle collides with the one + * Elementor and others register, making this a silent no-op on those sites. + * Reuse the theme's handle so both screens load the same file once. + */ + wp_enqueue_style( 'shapely-font-awesome', get_template_directory_uri() . '/assets/css/fontawesome6/all.min.css', array(), '6.4.2' ); wp_enqueue_style( 'epsilon-styles', EPSILON_URI . '/assets/css/style.css' ); } From 5ec0edc928098ebcf99718db762e93ff3d95f6c8 Mon Sep 17 00:00:00 2001 From: Aigars Silkalns Date: Tue, 11 Aug 2026 10:28:50 +0300 Subject: [PATCH 4/4] Allow the PHPCS Composer plugin so the coding-standards job can run Same failure as the companion plugin's CI: phpcodesniffer-composer-installer is a Composer plugin, and current Composer refuses to execute plugins that are not explicitly allow-listed. The require aborted with "contains a Composer plugin which is blocked by your allow-plugins config" before PHPCS was installed, so the coding-standards job had been failing on infrastructure rather than on anything in the theme. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3a75ca..b7922a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,11 @@ jobs: - name: Install PHPCS + standards run: | + # phpcodesniffer-composer-installer is a Composer plugin, and Composer + # refuses to run plugins that are not explicitly allow-listed. Without + # this the require aborts before PHPCS is ever installed. + composer global config --no-plugins --no-interaction \ + allow-plugins.dealerdirect/phpcodesniffer-composer-installer true composer global require --no-interaction --no-progress \ squizlabs/php_codesniffer \ phpcompatibility/php-compatibility \