feat(08-02): sanitize additional work data and generate email sections

- Added sanitization for additional_work array in sanitize_submission()
- Keys sanitized with sanitize_key(), values with sanitize_text_field()
- Added sanitization for sonstiges with sanitize_textarea_field()
- Added generate_additional_work_sections() to generate email tables
- Added has_additional_work_data() helper to check if section has data
- Added generate_sonstiges_section() for Sonstiges textarea in email
- Email tables use legacy bgcolor='#CCCCCC' header pattern
- All 4 field types (checkbox, abbau_aufbau, checkbox_anzahl, text) handled
- Empty sections omitted from email for clean formatting
- Line breaks preserved in Sonstiges with nl2br()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 23:00:39 +09:00
parent d0edef9c00
commit 270349b82f
2 changed files with 162 additions and 0 deletions

View File

@@ -38,6 +38,14 @@ class Umzugsliste_Email_Generator {
// All rooms
$content .= self::generate_all_rooms( $data );
// Additional work sections
$content .= self::generate_additional_work_sections( $data );
// Sonstiges
if ( ! empty( $data['sonstiges'] ) ) {
$content .= self::generate_sonstiges_section( $data['sonstiges'] );
}
// Grand totals
$content .= self::generate_grand_totals( $data );
@@ -294,6 +302,142 @@ class Umzugsliste_Email_Generator {
</tr></tbody></table></div></div>";
}
/**
* Generate additional work sections
*
* @param array $data Form data
* @return string HTML
*/
private static function generate_additional_work_sections( $data ) {
$html = '';
$sections = Umzugsliste_Furniture_Data::get_additional_work();
foreach ( $sections as $section_key => $section_data ) {
// Only include section if it has data
if ( self::has_additional_work_data( $data, $section_key ) ) {
$html .= "<div class='row'>
<div class='large-12 columns' style='margin: 10px 0px; overflow-x: auto;'>
<table width='100%'>
<thead>
<tr>
<th align='left' bgcolor='#CCCCCC' colspan='2'>" . esc_html( $section_data['label'] ) . "</th>
</tr>
</thead>
<tbody>";
$section_submitted_data = $data['additional_work'][ $section_key ] ?? array();
foreach ( $section_data['fields'] as $field ) {
// Get field key
$field_key = ! empty( $field['key'] ) ? $field['key'] : sanitize_title( $field['name'] );
// Get field value
$field_value = $section_submitted_data[ $field_key ] ?? '';
// Render based on field type
switch ( $field['type'] ) {
case 'checkbox':
if ( 'ja' === $field_value ) {
$html .= '<tr>';
$html .= '<td>' . esc_html( $field['name'] ) . '</td>';
$html .= '<td>Ja</td>';
$html .= '</tr>';
}
break;
case 'abbau_aufbau':
if ( ! empty( $field_value ) ) {
$html .= '<tr>';
$html .= '<td>' . esc_html( $field['name'] ) . '</td>';
$html .= '<td>' . esc_html( $field_value ) . '</td>';
$html .= '</tr>';
}
break;
case 'checkbox_anzahl':
if ( 'ja' === $field_value ) {
$anzahl_value = $section_submitted_data[ $field_key . '_anzahl' ] ?? '';
$display_value = 'Ja';
if ( ! empty( $anzahl_value ) ) {
$display_value .= ' (Anzahl: ' . esc_html( $anzahl_value ) . ')';
}
$html .= '<tr>';
$html .= '<td>' . esc_html( $field['name'] ) . '</td>';
$html .= '<td>' . $display_value . '</td>';
$html .= '</tr>';
}
break;
case 'text':
if ( ! empty( $field_value ) ) {
$html .= '<tr>';
$html .= '<td>' . esc_html( $field['name'] ) . '</td>';
$html .= '<td>' . esc_html( $field_value ) . '</td>';
$html .= '</tr>';
}
break;
}
}
$html .= '</tbody></table></div></div>';
}
}
return $html;
}
/**
* Check if section has any data
*
* @param array $data Form data
* @param string $section_key Section key
* @return bool True if has data
*/
private static function has_additional_work_data( $data, $section_key ) {
if ( empty( $data['additional_work'][ $section_key ] ) ) {
return false;
}
$section_data = $data['additional_work'][ $section_key ];
if ( ! is_array( $section_data ) ) {
return false;
}
// Check if any value is non-empty
foreach ( $section_data as $value ) {
if ( ! empty( trim( $value ) ) ) {
return true;
}
}
return false;
}
/**
* Generate Sonstiges section
*
* @param string $sonstiges_text Sonstiges text
* @return string HTML
*/
private static function generate_sonstiges_section( $sonstiges_text ) {
return "<div class='row'>
<div class='large-12 columns' style='margin: 10px 0px; overflow-x: auto;'>
<table width='100%'>
<thead>
<tr>
<th align='left' bgcolor='#CCCCCC'>Sonstiges</th>
</tr>
</thead>
<tbody>
<tr>
<td>" . nl2br( esc_html( $sonstiges_text ) ) . "</td>
</tr>
</tbody>
</table>
</div>
</div>";
}
/**
* Wrap content in HTML document structure
*

View File

@@ -239,6 +239,24 @@ class Umzugsliste_Form_Handler {
}
}
// Sanitize additional work sections
if ( ! empty( $data['additional_work'] ) && is_array( $data['additional_work'] ) ) {
$sanitized['additional_work'] = array();
foreach ( $data['additional_work'] as $section_key => $section_data ) {
if ( is_array( $section_data ) ) {
$sanitized['additional_work'][ sanitize_key( $section_key ) ] = array();
foreach ( $section_data as $field_key => $value ) {
$sanitized['additional_work'][ sanitize_key( $section_key ) ][ sanitize_key( $field_key ) ] = sanitize_text_field( $value );
}
}
}
}
// Sanitize Sonstiges
if ( ! empty( $data['sonstiges'] ) ) {
$sanitized['sonstiges'] = sanitize_textarea_field( $data['sonstiges'] );
}
return $sanitized;
}