>
*/
public static function default_content(): array {
return array(
'panel1' => array(
'description' =>
'' . __( "Please enter one measurement, either in mmol/L (the UK / Australian / International standard) or mg/dL (used mainly in the US and parts of Europe) - we'll convert it automatically.", 'sugarcalc' ) . '
' .
'' . __( 'The default setting assumes you are a Type 1 Diabetic with at least a 2 hour gap since starting your last meal. Please set the converter to suit your personal situation before use.', 'sugarcalc' ) . '
' .
'' . __( 'This converter should only be used as a general guide and should never replace your personal medical advice. Everyone living with Diabetes is different, please always follow the guidance and target Blood Glucose Levels (BGLs) set by your GP or specialist.', 'sugarcalc' ) . '
',
),
'panel3a' => array(
'title' => __( 'Acceptable', 'sugarcalc' ),
'subtitle' => '',
'description' =>
'' . __( 'The Blood Glucose Level (BGL) you have entered indicates you are not likely to be in a Hypoglycaemic, Hyperglycaemic, or Ketoacidotic state.', 'sugarcalc' ) . '
',
'advice' => array(
__( 'Your blood sugar is within the healthy target range', 'sugarcalc' ),
__( 'Continue following your diabetes management plan', 'sugarcalc' ),
__( 'Maintain your current diet and exercise routine', 'sugarcalc' ),
__( 'Keep monitoring your readings regularly', 'sugarcalc' ),
__( 'Well done on maintaining good glucose control', 'sugarcalc' ),
),
),
'panel3b' => array(
'title' => __( 'WARNING! HYPOGLYCAEMIA!', 'sugarcalc' ),
'subtitle' => '',
'description' =>
'' . __( 'The Blood Glucose Level (BGL) you have entered indicates you may be in a Hypoglycaemic state.', 'sugarcalc' ) . '
' .
'' . __( 'This happens when your BGL drops below an acceptable level. This could be caused by many factors - extra exercise, alcohol, missing your regular meal or not consuming enough after taking insulin.', 'sugarcalc' ) . '
',
'advice' => array(
__( 'Your blood sugar is lower than the recommended range', 'sugarcalc' ),
__( 'If you feel symptoms (shaky, sweaty, confused), treat immediately', 'sugarcalc' ),
__( 'Consume 15-20g of fast-acting carbohydrates (e.g., glucose tablets, juice)', 'sugarcalc' ),
__( 'Wait 15 minutes and retest', 'sugarcalc' ),
__( 'If symptoms persist or worsen, seek medical attention', 'sugarcalc' ),
__( 'Contact your healthcare provider if low readings are frequent', 'sugarcalc' ),
),
),
'panel3c' => array(
'title' => __( 'WARNING! HYPERGLYCAEMIA!', 'sugarcalc' ),
'subtitle' => '',
'description' =>
'' . __( 'The Blood Glucose Level (BGL) you have entered indicates you may be in a Hyperglycaemic state.', 'sugarcalc' ) . '
' .
'' . __( 'This happens when your BGL rises above an acceptable level. This could be caused by many factors - insufficient insulin, over-consuming carbohydrates, insufficient exercise, illness, or even stress.', 'sugarcalc' ) . '
',
'advice' => array(
__( 'Your blood sugar is higher than the recommended range', 'sugarcalc' ),
__( 'Contact your healthcare provider for guidance', 'sugarcalc' ),
__( 'Review your recent food intake and physical activity', 'sugarcalc' ),
__( 'Ensure you are taking medications as prescribed', 'sugarcalc' ),
__( 'Stay hydrated and monitor your readings closely', 'sugarcalc' ),
),
),
'panel3d' => array(
'title' => __( 'WARNING! KETOACIDOSIS!', 'sugarcalc' ),
'subtitle' => __( 'Immediate medical attention may be required', 'sugarcalc' ),
'description' =>
'' . __( 'The Blood Glucose Level (BGL) you have entered indicates you may be in Ketoacidosis (DKA).', 'sugarcalc' ) . '
' .
'' . __( 'This happens when your body is breaking down fat that results in acidic ketones building up in your bloodstream.', 'sugarcalc' ) . '
',
'advice' => array(
__( 'This reading indicates a potentially dangerous condition', 'sugarcalc' ),
__( 'Seek immediate medical attention or call emergency services', 'sugarcalc' ),
__( 'Do not attempt to manage this on your own', 'sugarcalc' ),
__( 'Check for ketones if you have testing strips', 'sugarcalc' ),
__( 'Stay hydrated while waiting for medical help', 'sugarcalc' ),
),
),
'ui' => array(
'learn_more' => __( 'Learn more', 'sugarcalc' ),
'result_fallback' => __( 'Result', 'sugarcalc' ),
// #00052: the non-clinical and < 2 / > 30 mmol/L states. Added after
// seeding, so they reach existing sites through get_content()'s merge
// over these defaults.
'waiting' => __( 'Waiting ...', 'sugarcalc' ),
'invalid_reading' => __( 'Please enter a reading greater than 0 and no more than 50 mmol/L (900 mg/dL).', 'sugarcalc' ),
'warning_low' => __( 'This reading is extremely low. If accurate, seek immediate medical attention.', 'sugarcalc' ),
'warning_high' => __( 'This reading is extremely high. If accurate, seek immediate medical attention.', 'sugarcalc' ),
),
);
}
/**
* Return the default display mode ('normal').
*
* @return string Default mode slug.
*/
public static function default_mode(): string {
return 'normal';
}
/**
* Sanitise raw panel text data to a clean string array.
*
* @param array $data Raw input data to sanitise.
* @return array
*/
public static function sanitise( array $data ): array {
$clean = array();
foreach ( self::panel_keys() as $key ) {
$clean[ $key ] = isset( $data[ $key ] ) ? (string) $data[ $key ] : '';
}
return $clean;
}
/**
* Validate a mode string; returns default_mode() for unrecognised values.
*
* @param string $mode Mode string to validate ('normal' or 'default').
* @return string Validated mode slug.
*/
public static function sanitise_mode( string $mode ): string {
return in_array( $mode, array( 'normal', 'default' ), true ) ? $mode : self::default_mode();
}
/**
* Return the Normal panel texts from the database.
*
* @return array
*/
public static function get_normal(): array {
return self::fetch_panel_texts( self::OPTION_KEY_NORMAL );
}
/**
* Return the Default panel texts from the database.
*
* @return array
*/
public static function get_default(): array {
return self::fetch_panel_texts( self::OPTION_KEY_DEFAULT );
}
/**
* Return the current display mode, validated against allowed values.
*
* @return string Current mode slug ('normal' or 'default').
*/
public static function get_mode(): string {
return self::sanitise_mode( (string) get_option( self::OPTION_KEY_MODE, self::default_mode() ) );
}
/**
* Returns the texts to serve to the frontend.
* Loads Default texts first, then replaces each entry with the Normal text if non-empty.
* When mode is forced to 'default', skips Normal texts entirely.
*
* @return array
*/
public static function get(): array {
$result = self::get_default();
if ( self::get_mode() === 'default' ) {
return $result;
}
foreach ( self::get_normal() as $key => $value ) {
if ( '' !== $value ) {
$result[ $key ] = $value;
}
}
return $result;
}
/**
* Overlay mode-resolved body texts onto structured content: a non-empty
* body replaces the matching panel's description and nothing else.
* UI strings and unknown body keys are ignored. Pure — unit-testable.
*
* @param array> $content Structured content shaped like default_content().
* @param array $bodies Mode-resolved body texts keyed by panel key.
* @return array>
*/
public static function merge_content( array $content, array $bodies ): array {
foreach ( self::panel_keys() as $key ) {
$body = (string) ( $bodies[ $key ] ?? '' );
if ( '' !== $body && isset( $content[ $key ] ) ) {
$content[ $key ]['description'] = $body;
}
}
return $content;
}
/**
* Returns the full structured content to serve to the frontend
* (issue 00027): canonical gettext defaults, overlaid with stored meta
* (titles/subtitles/advice/UI), then with the mode-resolved body texts
* from the existing Normal/Default pipeline as description overrides.
*
* @return array>
*/
public static function get_content(): array {
$content = self::default_content();
foreach ( self::fetch_meta() as $key => $fields ) {
$content[ $key ] = array_merge( $content[ $key ], $fields );
}
return self::merge_content( $content, self::get() );
}
/**
* Persist structured default content: descriptions go to the existing
* Default body set (admin-visible in the DEV Panels page, mode-aware);
* titles/subtitles/advice/UI strings go to the meta option
* (not admin-editable).
*
* @param array> $content Structured content shaped like default_content().
*/
public static function save_default_content( array $content ): void {
update_option( self::OPTION_KEY_META, self::build_meta( $content ) );
self::save_default( self::extract_descriptions( $content ) );
}
/**
* Seed the canonical default content on plugin activation (issue 00027).
* Never overwrites: each option is written only when absent, so existing
* site data — including pre-00027 flat default bodies — survives
* re-activation untouched.
*/
public static function seed_default_content(): void {
$content = self::default_content();
if ( false === get_option( self::OPTION_KEY_META ) ) {
update_option( self::OPTION_KEY_META, self::build_meta( $content ) );
}
if ( false === get_option( self::OPTION_KEY_DEFAULT ) ) {
self::save_default( self::extract_descriptions( $content ) );
}
}
/**
* Build the non-description fields of structured content for storage
* in the meta option, keyed by meta key.
*
* @param array> $content Structured content shaped like default_content().
* @return array>
*/
private static function build_meta( array $content ): array {
$meta = array();
foreach ( self::meta_keys() as $key ) {
if ( ! isset( $content[ $key ] ) ) {
continue;
}
$fields = $content[ $key ];
unset( $fields['description'] );
$meta[ $key ] = $fields;
}
return $meta;
}
/**
* Extract the per-panel description strings from structured content.
*
* @param array> $content Structured content shaped like default_content().
* @return array Descriptions keyed by panel key.
*/
private static function extract_descriptions( array $content ): array {
$descriptions = array();
foreach ( self::panel_keys() as $key ) {
$descriptions[ $key ] = (string) ( $content[ $key ]['description'] ?? '' );
}
return $descriptions;
}
/**
* Persist the Normal panel texts to the database.
*
* @param array $data Pre-sanitised panel text strings.
*/
public static function save_normal( array $data ): void {
update_option( self::OPTION_KEY_NORMAL, self::normalise_panel_array( $data ) );
}
/**
* Persist the Default panel texts to the database.
*
* @param array $data Pre-sanitised panel text strings.
*/
public static function save_default( array $data ): void {
update_option( self::OPTION_KEY_DEFAULT, self::normalise_panel_array( $data ) );
}
/**
* Persist the display mode to the database.
*
* @param string $mode Mode slug to save ('normal' or 'default').
*/
public static function save_mode( string $mode ): void {
update_option( self::OPTION_KEY_MODE, self::sanitise_mode( $mode ) );
}
/**
* Load the stored meta content. Keys with a missing or non-array
* payload are skipped — the caller falls back to default_content()
* for those.
*
* @return array>
*/
private static function fetch_meta(): array {
$saved = get_option( self::OPTION_KEY_META, array() );
if ( ! is_array( $saved ) ) {
return array();
}
$result = array();
foreach ( self::meta_keys() as $key ) {
$fields = $saved[ $key ] ?? null;
if ( is_array( $fields ) ) {
$result[ $key ] = $fields;
}
}
return $result;
}
/**
* Load a panel texts option as a normalised string array.
*
* @param string $option_key WordPress option key to fetch.
* @return array
*/
private static function fetch_panel_texts( string $option_key ): array {
$saved = get_option( $option_key, array() );
if ( ! is_array( $saved ) ) {
$saved = array();
}
return self::normalise_panel_array( $saved );
}
/**
* Ensures every panel key is present as a plain string, in storage order.
*
* @param array $data Panel texts keyed by panel key.
* @return array
*/
private static function normalise_panel_array( array $data ): array {
$normalised = array();
foreach ( self::panel_keys() as $key ) {
$normalised[ $key ] = (string) ( $data[ $key ] ?? '' );
}
return $normalised;
}
}