Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
108 changes: 108 additions & 0 deletions includes/Core/Email_Reporting/Email_Log.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,114 @@ final class Email_Log {
const STATUS_FAILED = 'gsk_email_failed';
const STATUS_SCHEDULED = 'gsk_email_scheduled';

/**
* Extracts a normalized date range array from an email log post.
*
* @param mixed $email_log Potential email log post.
* @return array|null
*/
public static function get_date_range_from_log( $email_log ) {
$decoded = self::validate_and_decode_email_log( $email_log );
if ( null === $decoded ) {
return null;
}

$normalized = array();
$start = isset( $decoded['startDate'] ) ? self::format_reference_date( $decoded['startDate'] ) : null;
if ( $start ) {
$normalized['startDate'] = $start;
}

$send = isset( $decoded['sendDate'] ) ? self::format_reference_date( $decoded['sendDate'] ) : null;
if ( $send ) {
$normalized['endDate'] = $send;
}

$compare_start = isset( $decoded['compareStartDate'] ) ? self::format_reference_date( $decoded['compareStartDate'] ) : null;
if ( $compare_start ) {
$normalized['compareStartDate'] = $compare_start;
}

$compare_end = isset( $decoded['compareEndDate'] ) ? self::format_reference_date( $decoded['compareEndDate'] ) : null;
if ( $compare_end ) {
$normalized['compareEndDate'] = $compare_end;
}

if ( empty( $normalized['startDate'] ) || empty( $normalized['endDate'] ) ) {
return null;
}

return $normalized;
}

/**
* Validates an email log and returns decoded reference date metadata.
*
* @param mixed $email_log Potential email log post.
* @return array|null Decoded reference date metadata, or null on failure.
*/
protected static function validate_and_decode_email_log( $email_log ) {
if ( ! ( $email_log instanceof \WP_Post ) ) {
return null;
}

if ( self::POST_TYPE !== $email_log->post_type ) {
return null;
}

$raw = get_post_meta( $email_log->ID, self::META_REPORT_REFERENCE_DATES, true );
if ( empty( $raw ) ) {
return null;
}

if ( is_string( $raw ) ) {
$decoded = json_decode( $raw, true );
if ( JSON_ERROR_NONE !== json_last_error() ) {
return null;
}
} elseif ( is_array( $raw ) ) {
$decoded = $raw;
} else {
return null;
}

return $decoded;
}

/**
* Formats a timestamp or date string stored in reference date meta.
*
* @param mixed $value Date value.
* @return string|null
*/
protected static function format_reference_date( $value ) {
if ( '' === $value || null === $value ) {
return null;
}

if ( is_numeric( $value ) ) {
$timestamp = (int) $value;
if ( $timestamp <= 0 ) {
return null;
}
} else {
$timestamp = strtotime( (string) $value );
}

if ( false === $timestamp ) {
return null;
}

if ( function_exists( 'wp_timezone' ) && function_exists( 'wp_date' ) ) {
$timezone = wp_timezone();
if ( $timezone ) {
return wp_date( 'Y-m-d', $timestamp, $timezone );
}
}

return gmdate( 'Y-m-d', $timestamp );
}

/**
* Registers functionality through WordPress hooks.
*
Expand Down
256 changes: 256 additions & 0 deletions includes/Core/Email_Reporting/Email_Report_Data_Section_Part.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
<?php
/**
* Class Google\Site_Kit\Core\Email_Reporting\Email_Report_Data_Section_Part
*
* @package Google\Site_Kit\Core\Email_Reporting
* @copyright 2025 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/

namespace Google\Site_Kit\Core\Email_Reporting;

use InvalidArgumentException;

/**
* Single email report section part.
*
* @since n.e.x.t
* @access private
* @ignore
*/
class Email_Report_Data_Section_Part {

/**
* Unique section key.
*
* @var string
*/
private $section_key;

/**
* Section title.
*
* @var string
*/
private $title;

/**
* Labels for the section rows/series.
*
* @var array
*/
private $labels;

/**
* Values formatted as strings for output.
*
* @var array
*/
private $values;

/**
* Optional trends matching values.
*
* @var array|null
*/
private $trends;

/**
* Optional date range data.
*
* @var array|null
*/
private $date_range;

/**
* Optional dashboard deeplink URL.
*
* @var string|null
*/
private $dashboard_link;

/**
* Constructor.
*
* @param string $section_key Unique section key.
* @param string $title Section title.
* @param array $labels Labels (strings) for the section rows/series.
* @param array $values Values (already formatted to strings for output).
* @param array|null $trends Optional trends (strings) matching values.
* @param array|null $date_range Optional date range array with 'startDate' and 'endDate'.
* @param string|null $dashboard_link Optional dashboard deeplink.
*
* @throws InvalidArgumentException When validation fails.
*/
public function __construct(
$section_key,
$title,
array $labels,
array $values,
$trends = null,
$date_range = null,
$dashboard_link = null
) {
$section_key = is_string( $section_key ) ? $section_key : '';
$title = is_string( $title ) ? $title : '';

if ( '' === $section_key ) {
throw new InvalidArgumentException( 'section_key must be a non-empty string' );
}
if ( '' === $title ) {
throw new InvalidArgumentException( 'title must be a non-empty string' );
}

$this->labels = array_map( 'strval', $labels );
$this->values = array_map( 'strval', $values );

if ( ! is_array( $labels ) ) {
throw new InvalidArgumentException( 'labels must be an array' );
}
if ( ! is_array( $values ) ) {
throw new InvalidArgumentException( 'values must be an array' );
}

if ( null !== $trends ) {
if ( ! is_array( $trends ) ) {
throw new InvalidArgumentException( 'trends must be an array or null' );
}
$this->trends = array_map( 'strval', $trends );
} else {
$this->trends = null;
}

if ( null !== $date_range ) {
if ( ! is_array( $date_range ) ) {
throw new InvalidArgumentException( 'date_range must be an array or null' );
}
// Validate presence of keys if provided.
$start = isset( $date_range['startDate'] ) ? (string) $date_range['startDate'] : null;
$end = isset( $date_range['endDate'] ) ? (string) $date_range['endDate'] : null;
if ( null === $start || null === $end ) {
throw new InvalidArgumentException( 'date_range must contain startDate and endDate' );
}
$compare_start_provided = array_key_exists( 'compareStartDate', $date_range );
$compare_end_provided = array_key_exists( 'compareEndDate', $date_range );
if ( $compare_start_provided xor $compare_end_provided ) {
throw new InvalidArgumentException( 'date_range must contain both compareStartDate and compareEndDate when comparison dates are provided' );
}
$this->date_range = array(
'startDate' => $start,
'endDate' => $end,
);
if ( $compare_start_provided && $compare_end_provided ) {
$this->date_range['compareStartDate'] = (string) $date_range['compareStartDate'];
$this->date_range['compareEndDate'] = (string) $date_range['compareEndDate'];
}
} else {
$this->date_range = null;
}

if ( null !== $dashboard_link && ! is_string( $dashboard_link ) ) {
throw new InvalidArgumentException( 'dashboard_link must be a string or null' );
}

$this->section_key = $section_key;
$this->title = $title;
$this->dashboard_link = $dashboard_link;
}

/**
* Gets the section key.
*
* @return string
*/
public function get_section_key() {
return $this->section_key;
}

/**
* Gets the title.
*
* @return string
*/
public function get_title() {
return $this->title;
}

/**
* Gets labels.
*
* @return array
*/
public function get_labels() {
return $this->labels;
}

/**
* Gets values.
*
* @return array
*/
public function get_values() {
return $this->values;
}

/**
* Gets trends.
*
* @return array|null
*/
public function get_trends() {
return $this->trends;
}

/**
* Gets date range.
*
* @return array|null
*/
public function get_date_range() {
return $this->date_range;
}

/**
* Gets dashboard link.
*
* @return string|null
*/
public function get_dashboard_link() {
return $this->dashboard_link;
}

/**
* Whether the section is empty (no values or all empty strings).
*
* @return bool
*/
public function is_empty() {
if ( empty( $this->values ) ) {
return true;
}
foreach ( $this->values as $value ) {
if ( '' !== trim( (string) $value ) ) {
return false;
}
}
return true;
}

/**
* Returns normalized array representation for templates.
*
* @return array
*/
public function to_array() {
return array(
'section_key' => $this->section_key,
'title' => $this->title,
'labels' => $this->labels,
'values' => $this->values,
'trends' => $this->trends,
'date_range' => $this->date_range,
'dashboard_link' => $this->dashboard_link,
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't seem to find a use for this method (not referenced anywhere outside of one test), and it seems redundant since we have exposed getters for each class property 🤔

}
Loading