From d6608db0dda5633367a72be12fdf9efa979ae0d2 Mon Sep 17 00:00:00 2001 From: "Matthew M. Emma" Date: Sat, 8 Aug 2026 16:21:47 -0700 Subject: [PATCH] Hide table columns that are empty for every row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Email suppression is on by default, so [procore_team] rendered an Email header above a blank cell for every member. On a default install that is visible dead weight on the most commonly used shortcode. Columns that render nothing for any row are now dropped before the table is built. That also covers a column no record happens to populate — a description or phone nobody has filled in — and the filter never reduces a table to no columns at all. Found by standing up a real WordPress site, serving it over HTTP and reading what a visitor actually receives, rather than by asserting on strings. The served markup was: headers: Name | Role | Company | Email email cells: 4 total, 0 non-empty and is now: headers: Name | Role | Company Two tests cover it: the column disappears when suppressed, and survives when both email opt-ins are on. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 14 ++++ procore-connect.php | 4 +- readme.txt | 9 ++- .../Shortcodes/CollectionShortcode.php | 40 ++++++++++- tests/unit/ShortcodeTest.php | 70 +++++++++++++++++++ 5 files changed, 133 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1ce21a..0ffa9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to Procore Connect are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.5] — 2026-08-08 + +### Fixed + +- **`[procore_team]` rendered an empty Email column.** Email suppression is on by + default, so the Email header appeared above a blank cell for every team member — + visible dead weight on the most commonly used shortcode, on every default install. + Columns that render nothing for any row are now dropped. This also covers a column + no record happens to populate, and a table is never reduced to no columns at all. + + Found by standing up a real WordPress site, serving it over HTTP and reading what a + visitor actually gets, rather than by asserting on strings. + ## [2.0.4] — 2026-08-08 ### Security @@ -231,6 +244,7 @@ every install was non-functional regardless of configuration. - Initial release. +[2.0.5]: https://github.com/ibuilder/ProcoreWP/releases/tag/v2.0.5 [2.0.4]: https://github.com/ibuilder/ProcoreWP/releases/tag/v2.0.4 [2.0.3]: https://github.com/ibuilder/ProcoreWP/releases/tag/v2.0.3 [2.0.2]: https://github.com/ibuilder/ProcoreWP/releases/tag/v2.0.2 diff --git a/procore-connect.php b/procore-connect.php index db5b2ed..8b8f6cd 100644 --- a/procore-connect.php +++ b/procore-connect.php @@ -3,7 +3,7 @@ * Plugin Name: Procore Connect * Plugin URI: https://github.com/ibuilder/ProcoreWP * Description: Connect WordPress to the Procore construction management platform. Display projects, teams, drawings, RFIs and more with shortcodes, blocks and a cached REST proxy. - * Version: 2.0.4 + * Version: 2.0.5 * Requires at least: 6.5 * Requires PHP: 7.4 * Author: ibuilder @@ -22,7 +22,7 @@ defined( 'ABSPATH' ) || exit; -const VERSION = '2.0.4'; +const VERSION = '2.0.5'; define( 'PROCORE_CONNECT_VERSION', VERSION ); define( 'PROCORE_CONNECT_FILE', __FILE__ ); diff --git a/readme.txt b/readme.txt index 22e633a..6268a23 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: procore, construction, project management, shortcode, api Requires at least: 6.5 Tested up to: 7.0 Requires PHP: 7.4 -Stable tag: 2.0.4 +Stable tag: 2.0.5 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -127,6 +127,10 @@ Yes. Caching goes through the transient API, so a persistent object cache such a == Changelog == += 2.0.5 = + +* Fixed: `[procore_team]` showed an empty Email column. Email output is suppressed by default, so the column appeared with a header and blank cells on every default install. Columns with no value in any row are now hidden. + = 2.0.4 = * Security documentation no longer recommends 2.0.0 or 2.0.1, the versions that damage the stored Client Secret. Both are now marked unsupported with the remedy stated plainly. @@ -183,6 +187,9 @@ A complete rewrite. See the upgrade notice below before updating. == Upgrade Notice == += 2.0.5 = +Cosmetic fix: the team table no longer shows an empty Email column when email output is suppressed, which is the default. + = 2.0.4 = Documentation and test coverage only. No functional change from 2.0.3. diff --git a/src/Frontend/Shortcodes/CollectionShortcode.php b/src/Frontend/Shortcodes/CollectionShortcode.php index ed8f936..c2b8135 100644 --- a/src/Frontend/Shortcodes/CollectionShortcode.php +++ b/src/Frontend/Shortcodes/CollectionShortcode.php @@ -54,7 +54,7 @@ protected function output( $data, array $atts ): string { $this->template( $atts ), array( 'rows' => $rows, - 'columns' => $this->resolve_columns( $atts ), + 'columns' => $this->drop_empty_columns( $this->resolve_columns( $atts ), $rows, ! empty( $atts['show_email'] ) ), 'title' => $this->title( $atts ), 'class' => Format::classes( 'procore-connect procore-connect-collection procore-connect-' . str_replace( '_', '-', $this->endpoint ), (string) $atts['class'] ), 'show_email' => ! empty( $atts['show_email'] ), @@ -64,6 +64,44 @@ protected function output( $data, array $atts ): string { ); } + /** + * Remove columns that render nothing for every row. + * + * The visible case is `[procore_team]`: email suppression is on by default, + * so the Email column produced a header and a blank cell for every member — + * dead weight on the most commonly used shortcode. This also covers a + * column no record happens to populate. + * + * A column an author asked for explicitly is always kept, so `columns=""` + * still means what it says. + * + * @param array> $columns Column definitions. + * @param array $rows Records being rendered. + * @param bool $show_email Whether the shortcode opted in to email output. + * @return array> Columns that carry at least one value. + */ + protected function drop_empty_columns( array $columns, array $rows, bool $show_email ): array { + $kept = array(); + + foreach ( $columns as $column ) { + $has_value = false; + + foreach ( $rows as $row ) { + if ( '' !== trim( Format::cell( $row, $column, $show_email ) ) ) { + $has_value = true; + break; + } + } + + if ( $has_value ) { + $kept[] = $column; + } + } + + // Never render a table with no columns at all. + return empty( $kept ) ? $columns : $kept; + } + /** * The field to sort on, honouring the ProcoreWP 1.x `sort_by` alias. * diff --git a/tests/unit/ShortcodeTest.php b/tests/unit/ShortcodeTest.php index eba1fc4..72b0d73 100644 --- a/tests/unit/ShortcodeTest.php +++ b/tests/unit/ShortcodeTest.php @@ -293,6 +293,76 @@ public function test_team_hides_emails_by_default(): void { $this->assertStringNotContainsString( 'dana@example.com', $html ); } + /** + * A column that renders nothing for every row must not be shown. + * + * Email suppression is on by default, so `[procore_team]` used to emit an + * Email header and a blank cell for every member. + * + * @return void + */ + public function test_drops_a_column_that_is_empty_for_every_row(): void { + $this->client_returning( + array( + $this->response( + array( + array( + 'id' => 3, + 'name' => 'Dana Reed', + 'job_title' => 'PM', + 'email_address' => 'dana@example.com', + ), + ) + ), + ) + ); + + $html = $this->shortcode( 'procore_team' )->render( + array( + 'project_id' => '5', + 'company_id' => '9', + ) + ); + + $this->assertStringContainsString( 'Dana Reed', $html ); + $this->assertStringNotContainsString( '>Email<', $html ); + } + + /** + * A column with data must survive the empty-column filter. + * + * @return void + */ + public function test_keeps_columns_that_carry_a_value(): void { + Settings::set( 'suppress_emails', false ); + + $this->client_returning( + array( + $this->response( + array( + array( + 'id' => 3, + 'name' => 'Dana Reed', + 'job_title' => 'PM', + 'email_address' => 'dana@example.com', + ), + ) + ), + ) + ); + + $html = $this->shortcode( 'procore_team' )->render( + array( + 'project_id' => '5', + 'company_id' => '9', + 'show_email' => 'true', + ) + ); + + $this->assertStringContainsString( '>Email<', $html ); + $this->assertStringContainsString( 'dana@example.com', $html ); + } + /** * A field outside the allow-list must be refused. *